tabctl 0.3.0 → 0.4.0
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 +7 -5
- package/dist/cli/lib/client.js +23 -2
- package/dist/cli/lib/commands/index.js +2 -1
- package/dist/cli/lib/commands/meta.js +16 -13
- package/dist/cli/lib/commands/params-groups.js +8 -0
- package/dist/cli/lib/commands/params.js +6 -3
- package/dist/cli/lib/commands/setup.js +104 -126
- package/dist/cli/lib/options-commands.js +13 -1
- package/dist/cli/lib/output.js +36 -1
- package/dist/cli/lib/policy-filter.js +1 -1
- package/dist/cli/lib/policy.js +3 -3
- package/dist/cli/lib/response.js +9 -9
- package/dist/cli/tabctl.js +9 -1
- package/dist/extension/background.js +429 -43
- package/dist/extension/lib/groups.js +89 -3
- package/dist/extension/lib/screenshot.js +2 -2
- package/dist/extension/lib/tabs.js +97 -36
- package/dist/extension/lib/undo-handlers.js +8 -0
- package/dist/extension/manifest.json +2 -2
- package/dist/host/host.bundle.js +669 -0
- package/dist/host/host.js +30 -11
- package/dist/host/launcher/go.mod +3 -0
- package/dist/host/launcher/main.go +109 -0
- package/dist/host/lib/handlers.js +7 -5
- package/dist/host/lib/undo.js +6 -6
- package/dist/shared/config.js +36 -13
- package/dist/shared/extension-sync.js +59 -15
- package/dist/shared/profiles.js +5 -5
- package/dist/shared/version.js +2 -2
- package/package.json +11 -4
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
(() => {
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
2
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
3
9
|
var __commonJS = (cb, mod) => function __require() {
|
|
4
10
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
5
11
|
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
6
25
|
|
|
7
26
|
// dist/extension/lib/screenshot.js
|
|
8
27
|
var require_screenshot = __commonJS({
|
|
@@ -690,8 +709,13 @@
|
|
|
690
709
|
exports.groupUpdate = groupUpdate2;
|
|
691
710
|
exports.groupUngroup = groupUngroup2;
|
|
692
711
|
exports.groupAssign = groupAssign2;
|
|
712
|
+
exports.groupGather = groupGather2;
|
|
693
713
|
function getGroupTabs(windowSnapshot, groupId) {
|
|
694
|
-
return windowSnapshot.tabs.filter((tab) => tab.groupId === groupId).sort((a, b) =>
|
|
714
|
+
return windowSnapshot.tabs.filter((tab) => tab.groupId === groupId).sort((a, b) => {
|
|
715
|
+
const ai = Number(a.index);
|
|
716
|
+
const bi = Number(b.index);
|
|
717
|
+
return (Number.isFinite(ai) ? ai : 0) - (Number.isFinite(bi) ? bi : 0);
|
|
718
|
+
});
|
|
695
719
|
}
|
|
696
720
|
function listGroupSummaries(snapshot, buildWindowLabels2, windowId) {
|
|
697
721
|
const windowLabels = buildWindowLabels2(snapshot);
|
|
@@ -758,8 +782,8 @@
|
|
|
758
782
|
if (matches.length > 1) {
|
|
759
783
|
return {
|
|
760
784
|
error: {
|
|
761
|
-
message:
|
|
762
|
-
hint: "Use --window to
|
|
785
|
+
message: `Ambiguous group title: found ${matches.length} groups named "${groupTitle}". Use group-gather to merge duplicates, --group-id to target by ID, or --window to narrow scope.`,
|
|
786
|
+
hint: "Use group-gather to merge duplicates, --group-id to target by ID, or --window to narrow scope.",
|
|
763
787
|
matches: matches.map((match) => summarizeGroupMatch(match, windowLabels)),
|
|
764
788
|
availableGroups
|
|
765
789
|
}
|
|
@@ -1105,6 +1129,301 @@
|
|
|
1105
1129
|
txid: params.txid || null
|
|
1106
1130
|
};
|
|
1107
1131
|
}
|
|
1132
|
+
async function groupGather2(params, deps2) {
|
|
1133
|
+
const snapshot = await deps2.getTabSnapshot();
|
|
1134
|
+
const windows = snapshot.windows;
|
|
1135
|
+
const windowIdParam = params.windowId != null ? deps2.resolveWindowIdFromParams(snapshot, params.windowId) : null;
|
|
1136
|
+
if (windowIdParam && !windows.some((win) => win.windowId === windowIdParam)) {
|
|
1137
|
+
throw new Error("Window not found");
|
|
1138
|
+
}
|
|
1139
|
+
const groupTitleFilter = typeof params.groupTitle === "string" ? params.groupTitle.trim() : "";
|
|
1140
|
+
const merged = [];
|
|
1141
|
+
const undoEntries = [];
|
|
1142
|
+
for (const win of windows) {
|
|
1143
|
+
if (windowIdParam && win.windowId !== windowIdParam)
|
|
1144
|
+
continue;
|
|
1145
|
+
const byTitle = /* @__PURE__ */ new Map();
|
|
1146
|
+
for (const group of win.groups) {
|
|
1147
|
+
const title = typeof group.title === "string" ? group.title : "";
|
|
1148
|
+
if (!title)
|
|
1149
|
+
continue;
|
|
1150
|
+
if (groupTitleFilter && title !== groupTitleFilter)
|
|
1151
|
+
continue;
|
|
1152
|
+
if (!byTitle.has(title))
|
|
1153
|
+
byTitle.set(title, []);
|
|
1154
|
+
byTitle.get(title).push(group);
|
|
1155
|
+
}
|
|
1156
|
+
for (const [title, titleGroups] of byTitle) {
|
|
1157
|
+
if (titleGroups.length < 2)
|
|
1158
|
+
continue;
|
|
1159
|
+
const groupsWithIndex = titleGroups.map((g) => {
|
|
1160
|
+
const tabs2 = win.tabs.filter((t) => t.groupId === g.groupId);
|
|
1161
|
+
const minIndex = Math.min(...tabs2.map((t) => {
|
|
1162
|
+
const idx = Number(t.index);
|
|
1163
|
+
return Number.isFinite(idx) ? idx : Infinity;
|
|
1164
|
+
}));
|
|
1165
|
+
return { group: g, tabs: tabs2, minIndex };
|
|
1166
|
+
});
|
|
1167
|
+
groupsWithIndex.sort((a, b) => a.minIndex - b.minIndex);
|
|
1168
|
+
const primary = groupsWithIndex[0];
|
|
1169
|
+
const duplicates = groupsWithIndex.slice(1);
|
|
1170
|
+
let movedTabs = 0;
|
|
1171
|
+
for (const dup of duplicates) {
|
|
1172
|
+
const tabIds = dup.tabs.map((t) => t.tabId).filter((id) => typeof id === "number");
|
|
1173
|
+
if (tabIds.length > 0) {
|
|
1174
|
+
for (const tab of dup.tabs) {
|
|
1175
|
+
undoEntries.push({
|
|
1176
|
+
tabId: tab.tabId,
|
|
1177
|
+
windowId: win.windowId,
|
|
1178
|
+
index: tab.index,
|
|
1179
|
+
groupId: tab.groupId,
|
|
1180
|
+
groupTitle: tab.groupTitle,
|
|
1181
|
+
groupColor: tab.groupColor,
|
|
1182
|
+
groupCollapsed: dup.group.collapsed ?? null
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
await chrome.tabs.group({ groupId: primary.group.groupId, tabIds });
|
|
1186
|
+
movedTabs += tabIds.length;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
merged.push({
|
|
1190
|
+
windowId: win.windowId,
|
|
1191
|
+
groupTitle: title,
|
|
1192
|
+
primaryGroupId: primary.group.groupId,
|
|
1193
|
+
mergedGroupCount: duplicates.length,
|
|
1194
|
+
movedTabs
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
return {
|
|
1199
|
+
merged,
|
|
1200
|
+
summary: {
|
|
1201
|
+
mergedGroups: merged.reduce((sum, m) => sum + m.mergedGroupCount, 0),
|
|
1202
|
+
movedTabs: merged.reduce((sum, m) => sum + m.movedTabs, 0)
|
|
1203
|
+
},
|
|
1204
|
+
undo: {
|
|
1205
|
+
action: "group-gather",
|
|
1206
|
+
tabs: undoEntries
|
|
1207
|
+
},
|
|
1208
|
+
txid: params.txid || null
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
// node_modules/normalize-url/index.js
|
|
1215
|
+
var normalize_url_exports = {};
|
|
1216
|
+
__export(normalize_url_exports, {
|
|
1217
|
+
default: () => normalizeUrl
|
|
1218
|
+
});
|
|
1219
|
+
function normalizeUrl(urlString, options) {
|
|
1220
|
+
options = {
|
|
1221
|
+
defaultProtocol: "http",
|
|
1222
|
+
normalizeProtocol: true,
|
|
1223
|
+
forceHttp: false,
|
|
1224
|
+
forceHttps: false,
|
|
1225
|
+
stripAuthentication: true,
|
|
1226
|
+
stripHash: false,
|
|
1227
|
+
stripTextFragment: true,
|
|
1228
|
+
stripWWW: true,
|
|
1229
|
+
removeQueryParameters: [/^utm_\w+/i],
|
|
1230
|
+
removeTrailingSlash: true,
|
|
1231
|
+
removeSingleSlash: true,
|
|
1232
|
+
removeDirectoryIndex: false,
|
|
1233
|
+
removeExplicitPort: false,
|
|
1234
|
+
sortQueryParameters: true,
|
|
1235
|
+
removePath: false,
|
|
1236
|
+
transformPath: false,
|
|
1237
|
+
...options
|
|
1238
|
+
};
|
|
1239
|
+
if (typeof options.defaultProtocol === "string" && !options.defaultProtocol.endsWith(":")) {
|
|
1240
|
+
options.defaultProtocol = `${options.defaultProtocol}:`;
|
|
1241
|
+
}
|
|
1242
|
+
urlString = urlString.trim();
|
|
1243
|
+
if (/^data:/i.test(urlString)) {
|
|
1244
|
+
return normalizeDataURL(urlString, options);
|
|
1245
|
+
}
|
|
1246
|
+
if (hasCustomProtocol(urlString)) {
|
|
1247
|
+
return urlString;
|
|
1248
|
+
}
|
|
1249
|
+
const hasRelativeProtocol = urlString.startsWith("//");
|
|
1250
|
+
const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString);
|
|
1251
|
+
if (!isRelativeUrl) {
|
|
1252
|
+
urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol);
|
|
1253
|
+
}
|
|
1254
|
+
const urlObject = new URL(urlString);
|
|
1255
|
+
if (options.forceHttp && options.forceHttps) {
|
|
1256
|
+
throw new Error("The `forceHttp` and `forceHttps` options cannot be used together");
|
|
1257
|
+
}
|
|
1258
|
+
if (options.forceHttp && urlObject.protocol === "https:") {
|
|
1259
|
+
urlObject.protocol = "http:";
|
|
1260
|
+
}
|
|
1261
|
+
if (options.forceHttps && urlObject.protocol === "http:") {
|
|
1262
|
+
urlObject.protocol = "https:";
|
|
1263
|
+
}
|
|
1264
|
+
if (options.stripAuthentication) {
|
|
1265
|
+
urlObject.username = "";
|
|
1266
|
+
urlObject.password = "";
|
|
1267
|
+
}
|
|
1268
|
+
if (options.stripHash) {
|
|
1269
|
+
urlObject.hash = "";
|
|
1270
|
+
} else if (options.stripTextFragment) {
|
|
1271
|
+
urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, "");
|
|
1272
|
+
}
|
|
1273
|
+
if (urlObject.pathname) {
|
|
1274
|
+
const protocolRegex = /\b[a-z][a-z\d+\-.]{1,50}:\/\//g;
|
|
1275
|
+
let lastIndex = 0;
|
|
1276
|
+
let result = "";
|
|
1277
|
+
for (; ; ) {
|
|
1278
|
+
const match = protocolRegex.exec(urlObject.pathname);
|
|
1279
|
+
if (!match) {
|
|
1280
|
+
break;
|
|
1281
|
+
}
|
|
1282
|
+
const protocol = match[0];
|
|
1283
|
+
const protocolAtIndex = match.index;
|
|
1284
|
+
const intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex);
|
|
1285
|
+
result += intermediate.replace(/\/{2,}/g, "/");
|
|
1286
|
+
result += protocol;
|
|
1287
|
+
lastIndex = protocolAtIndex + protocol.length;
|
|
1288
|
+
}
|
|
1289
|
+
const remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length);
|
|
1290
|
+
result += remnant.replace(/\/{2,}/g, "/");
|
|
1291
|
+
urlObject.pathname = result;
|
|
1292
|
+
}
|
|
1293
|
+
if (urlObject.pathname) {
|
|
1294
|
+
try {
|
|
1295
|
+
urlObject.pathname = decodeURI(urlObject.pathname).replace(/\\/g, "%5C");
|
|
1296
|
+
} catch {
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
if (options.removeDirectoryIndex === true) {
|
|
1300
|
+
options.removeDirectoryIndex = [/^index\.[a-z]+$/];
|
|
1301
|
+
}
|
|
1302
|
+
if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) {
|
|
1303
|
+
const pathComponents = urlObject.pathname.split("/").filter(Boolean);
|
|
1304
|
+
const lastComponent = pathComponents.at(-1);
|
|
1305
|
+
if (lastComponent && testParameter(lastComponent, options.removeDirectoryIndex)) {
|
|
1306
|
+
pathComponents.pop();
|
|
1307
|
+
urlObject.pathname = pathComponents.length > 0 ? `/${pathComponents.join("/")}/` : "/";
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
if (options.removePath) {
|
|
1311
|
+
urlObject.pathname = "/";
|
|
1312
|
+
}
|
|
1313
|
+
if (options.transformPath && typeof options.transformPath === "function") {
|
|
1314
|
+
const pathComponents = urlObject.pathname.split("/").filter(Boolean);
|
|
1315
|
+
const newComponents = options.transformPath(pathComponents);
|
|
1316
|
+
urlObject.pathname = newComponents?.length > 0 ? `/${newComponents.join("/")}` : "/";
|
|
1317
|
+
}
|
|
1318
|
+
if (urlObject.hostname) {
|
|
1319
|
+
urlObject.hostname = urlObject.hostname.replace(/\.$/, "");
|
|
1320
|
+
if (options.stripWWW && /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname)) {
|
|
1321
|
+
urlObject.hostname = urlObject.hostname.replace(/^www\./, "");
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
if (Array.isArray(options.removeQueryParameters)) {
|
|
1325
|
+
for (const key of [...urlObject.searchParams.keys()]) {
|
|
1326
|
+
if (testParameter(key, options.removeQueryParameters)) {
|
|
1327
|
+
urlObject.searchParams.delete(key);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
if (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) {
|
|
1332
|
+
urlObject.search = "";
|
|
1333
|
+
}
|
|
1334
|
+
if (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) {
|
|
1335
|
+
for (const key of [...urlObject.searchParams.keys()]) {
|
|
1336
|
+
if (!testParameter(key, options.keepQueryParameters)) {
|
|
1337
|
+
urlObject.searchParams.delete(key);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
if (options.sortQueryParameters) {
|
|
1342
|
+
const originalSearch = urlObject.search;
|
|
1343
|
+
urlObject.searchParams.sort();
|
|
1344
|
+
try {
|
|
1345
|
+
urlObject.search = decodeURIComponent(urlObject.search);
|
|
1346
|
+
} catch {
|
|
1347
|
+
}
|
|
1348
|
+
const partsWithoutEquals = originalSearch.slice(1).split("&").filter((p) => p && !p.includes("="));
|
|
1349
|
+
for (const part of partsWithoutEquals) {
|
|
1350
|
+
const decoded = decodeURIComponent(part);
|
|
1351
|
+
urlObject.search = urlObject.search.replace(`?${decoded}=`, `?${decoded}`).replace(`&${decoded}=`, `&${decoded}`);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
if (options.removeTrailingSlash) {
|
|
1355
|
+
urlObject.pathname = urlObject.pathname.replace(/\/$/, "");
|
|
1356
|
+
}
|
|
1357
|
+
if (options.removeExplicitPort && urlObject.port) {
|
|
1358
|
+
urlObject.port = "";
|
|
1359
|
+
}
|
|
1360
|
+
const oldUrlString = urlString;
|
|
1361
|
+
urlString = urlObject.toString();
|
|
1362
|
+
if (!options.removeSingleSlash && urlObject.pathname === "/" && !oldUrlString.endsWith("/") && urlObject.hash === "") {
|
|
1363
|
+
urlString = urlString.replace(/\/$/, "");
|
|
1364
|
+
}
|
|
1365
|
+
if ((options.removeTrailingSlash || urlObject.pathname === "/") && urlObject.hash === "" && options.removeSingleSlash) {
|
|
1366
|
+
urlString = urlString.replace(/\/$/, "");
|
|
1367
|
+
}
|
|
1368
|
+
if (hasRelativeProtocol && !options.normalizeProtocol) {
|
|
1369
|
+
urlString = urlString.replace(/^http:\/\//, "//");
|
|
1370
|
+
}
|
|
1371
|
+
if (options.stripProtocol) {
|
|
1372
|
+
urlString = urlString.replace(/^(?:https?:)?\/\//, "");
|
|
1373
|
+
}
|
|
1374
|
+
return urlString;
|
|
1375
|
+
}
|
|
1376
|
+
var DATA_URL_DEFAULT_MIME_TYPE, DATA_URL_DEFAULT_CHARSET, testParameter, supportedProtocols, hasCustomProtocol, normalizeDataURL;
|
|
1377
|
+
var init_normalize_url = __esm({
|
|
1378
|
+
"node_modules/normalize-url/index.js"() {
|
|
1379
|
+
DATA_URL_DEFAULT_MIME_TYPE = "text/plain";
|
|
1380
|
+
DATA_URL_DEFAULT_CHARSET = "us-ascii";
|
|
1381
|
+
testParameter = (name, filters) => filters.some((filter) => filter instanceof RegExp ? filter.test(name) : filter === name);
|
|
1382
|
+
supportedProtocols = /* @__PURE__ */ new Set([
|
|
1383
|
+
"https:",
|
|
1384
|
+
"http:",
|
|
1385
|
+
"file:"
|
|
1386
|
+
]);
|
|
1387
|
+
hasCustomProtocol = (urlString) => {
|
|
1388
|
+
try {
|
|
1389
|
+
const { protocol } = new URL(urlString);
|
|
1390
|
+
return protocol.endsWith(":") && !protocol.includes(".") && !supportedProtocols.has(protocol);
|
|
1391
|
+
} catch {
|
|
1392
|
+
return false;
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
normalizeDataURL = (urlString, { stripHash }) => {
|
|
1396
|
+
const match = /^data:(?<type>[^,]*?),(?<data>[^#]*?)(?:#(?<hash>.*))?$/.exec(urlString);
|
|
1397
|
+
if (!match) {
|
|
1398
|
+
throw new Error(`Invalid URL: ${urlString}`);
|
|
1399
|
+
}
|
|
1400
|
+
const { type, data, hash } = match.groups;
|
|
1401
|
+
const mediaType = type.split(";");
|
|
1402
|
+
const isBase64 = mediaType.at(-1) === "base64";
|
|
1403
|
+
if (isBase64) {
|
|
1404
|
+
mediaType.pop();
|
|
1405
|
+
}
|
|
1406
|
+
const mimeType = mediaType.shift()?.toLowerCase() ?? "";
|
|
1407
|
+
const attributes = mediaType.map((attribute) => {
|
|
1408
|
+
let [key, value = ""] = attribute.split("=").map((string) => string.trim());
|
|
1409
|
+
if (key === "charset") {
|
|
1410
|
+
value = value.toLowerCase();
|
|
1411
|
+
if (value === DATA_URL_DEFAULT_CHARSET) {
|
|
1412
|
+
return "";
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
return `${key}${value ? `=${value}` : ""}`;
|
|
1416
|
+
}).filter(Boolean);
|
|
1417
|
+
const normalizedMediaType = [...attributes];
|
|
1418
|
+
if (isBase64) {
|
|
1419
|
+
normalizedMediaType.push("base64");
|
|
1420
|
+
}
|
|
1421
|
+
if (normalizedMediaType.length > 0 || mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE) {
|
|
1422
|
+
normalizedMediaType.unshift(mimeType);
|
|
1423
|
+
}
|
|
1424
|
+
const hashPart = stripHash || !hash ? "" : `#${hash}`;
|
|
1425
|
+
return `data:${normalizedMediaType.join(";")},${isBase64 ? data.trim() : data}${hashPart}`;
|
|
1426
|
+
};
|
|
1108
1427
|
}
|
|
1109
1428
|
});
|
|
1110
1429
|
|
|
@@ -1112,14 +1431,18 @@
|
|
|
1112
1431
|
var require_tabs = __commonJS({
|
|
1113
1432
|
"dist/extension/lib/tabs.js"(exports) {
|
|
1114
1433
|
"use strict";
|
|
1434
|
+
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
1435
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
1436
|
+
};
|
|
1115
1437
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1116
1438
|
exports.getMostRecentFocusedWindowId = getMostRecentFocusedWindowId2;
|
|
1117
|
-
exports.normalizeUrl =
|
|
1439
|
+
exports.normalizeUrl = normalizeUrl2;
|
|
1118
1440
|
exports.normalizeTabIndex = normalizeTabIndex2;
|
|
1119
1441
|
exports.resolveOpenWindow = resolveOpenWindow;
|
|
1120
1442
|
exports.focusTab = focusTab;
|
|
1121
1443
|
exports.refreshTabs = refreshTabs;
|
|
1122
1444
|
exports.openTabs = openTabs;
|
|
1445
|
+
var normalize_url_1 = __importDefault((init_normalize_url(), __toCommonJS(normalize_url_exports)));
|
|
1123
1446
|
function getMostRecentFocusedWindowId2(windows) {
|
|
1124
1447
|
let bestWindowId = null;
|
|
1125
1448
|
let bestFocusedAt = -Infinity;
|
|
@@ -1137,45 +1460,29 @@
|
|
|
1137
1460
|
}
|
|
1138
1461
|
return bestWindowId;
|
|
1139
1462
|
}
|
|
1140
|
-
function
|
|
1463
|
+
function normalizeUrl2(rawUrl) {
|
|
1141
1464
|
if (!rawUrl || typeof rawUrl !== "string") {
|
|
1142
1465
|
return null;
|
|
1143
1466
|
}
|
|
1144
|
-
let url;
|
|
1145
1467
|
try {
|
|
1146
|
-
|
|
1468
|
+
return (0, normalize_url_1.default)(rawUrl, {
|
|
1469
|
+
stripHash: true,
|
|
1470
|
+
removeQueryParameters: [
|
|
1471
|
+
/^utm_\w+$/i,
|
|
1472
|
+
"fbclid",
|
|
1473
|
+
"gclid",
|
|
1474
|
+
"igshid",
|
|
1475
|
+
"mc_cid",
|
|
1476
|
+
"mc_eid",
|
|
1477
|
+
"ref",
|
|
1478
|
+
"ref_src",
|
|
1479
|
+
"ref_url",
|
|
1480
|
+
"si"
|
|
1481
|
+
]
|
|
1482
|
+
});
|
|
1147
1483
|
} catch {
|
|
1148
1484
|
return null;
|
|
1149
1485
|
}
|
|
1150
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
1151
|
-
return null;
|
|
1152
|
-
}
|
|
1153
|
-
url.hash = "";
|
|
1154
|
-
const dropKeys = /* @__PURE__ */ new Set([
|
|
1155
|
-
"fbclid",
|
|
1156
|
-
"gclid",
|
|
1157
|
-
"igshid",
|
|
1158
|
-
"mc_cid",
|
|
1159
|
-
"mc_eid",
|
|
1160
|
-
"ref",
|
|
1161
|
-
"ref_src",
|
|
1162
|
-
"ref_url",
|
|
1163
|
-
"utm_campaign",
|
|
1164
|
-
"utm_content",
|
|
1165
|
-
"utm_medium",
|
|
1166
|
-
"utm_source",
|
|
1167
|
-
"utm_term",
|
|
1168
|
-
"utm_name",
|
|
1169
|
-
"si"
|
|
1170
|
-
]);
|
|
1171
|
-
for (const key of Array.from(url.searchParams.keys())) {
|
|
1172
|
-
if (key.startsWith("utm_") || dropKeys.has(key)) {
|
|
1173
|
-
url.searchParams.delete(key);
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
const search = url.searchParams.toString();
|
|
1177
|
-
url.search = search ? `?${search}` : "";
|
|
1178
|
-
return url.toString();
|
|
1179
1486
|
}
|
|
1180
1487
|
function normalizeTabIndex2(value) {
|
|
1181
1488
|
const index = Number(value);
|
|
@@ -1303,6 +1610,8 @@
|
|
|
1303
1610
|
throw new Error("Only one target position is allowed");
|
|
1304
1611
|
}
|
|
1305
1612
|
const newWindow = params.newWindow === true;
|
|
1613
|
+
const forceNewGroup = params.newGroup === true;
|
|
1614
|
+
const allowDuplicates = params.allowDuplicates === true;
|
|
1306
1615
|
if (!urls.length && !newWindow) {
|
|
1307
1616
|
throw new Error("No URLs provided");
|
|
1308
1617
|
}
|
|
@@ -1387,6 +1696,12 @@
|
|
|
1387
1696
|
}
|
|
1388
1697
|
const snapshot = await deps2.getTabSnapshot();
|
|
1389
1698
|
let openParams = params;
|
|
1699
|
+
if (groupTitle && !forceNewGroup && openParams.windowId == null && !openParams.windowGroupTitle && !openParams.windowTabId && !openParams.windowUrl) {
|
|
1700
|
+
const groupWindows = snapshot.windows.filter((win) => win.groups.some((g) => g.title === groupTitle));
|
|
1701
|
+
if (groupWindows.length === 1) {
|
|
1702
|
+
openParams = { ...openParams, windowId: groupWindows[0].windowId };
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1390
1705
|
if (params.windowId == null && (beforeTabId != null || afterTabId != null)) {
|
|
1391
1706
|
const anchorId = beforeTabId != null ? beforeTabId : afterTabId;
|
|
1392
1707
|
const anchorWindow = snapshot.windows.find((win) => win.tabs.some((tab) => tab.tabId === anchorId));
|
|
@@ -1403,6 +1718,24 @@
|
|
|
1403
1718
|
if (!windowSnapshot) {
|
|
1404
1719
|
throw new Error("Window snapshot unavailable");
|
|
1405
1720
|
}
|
|
1721
|
+
let existingGroupId = null;
|
|
1722
|
+
const existingUrlSet = /* @__PURE__ */ new Set();
|
|
1723
|
+
if (groupTitle && !forceNewGroup) {
|
|
1724
|
+
const matchingGroups = windowSnapshot.groups.filter((g) => g.title === groupTitle);
|
|
1725
|
+
if (matchingGroups.length > 1) {
|
|
1726
|
+
throw new Error(`Ambiguous group title "${groupTitle}": found ${matchingGroups.length} groups with the same name. Use --new-group to force a new group, group-gather to merge, or --group-id to target by ID.`);
|
|
1727
|
+
}
|
|
1728
|
+
if (matchingGroups.length === 1) {
|
|
1729
|
+
existingGroupId = matchingGroups[0].groupId;
|
|
1730
|
+
const existingTabs = windowSnapshot.tabs.filter((tab) => tab.groupId === existingGroupId);
|
|
1731
|
+
for (const tab of existingTabs) {
|
|
1732
|
+
const norm = normalizeUrl2(tab.url);
|
|
1733
|
+
if (norm) {
|
|
1734
|
+
existingUrlSet.add(norm);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1406
1739
|
const created = [];
|
|
1407
1740
|
const skipped = [];
|
|
1408
1741
|
let insertIndex = null;
|
|
@@ -1436,8 +1769,22 @@
|
|
|
1436
1769
|
}
|
|
1437
1770
|
insertIndex = beforeTabId != null ? anchorIndex : anchorIndex + 1;
|
|
1438
1771
|
}
|
|
1772
|
+
if (existingGroupId != null && insertIndex == null && beforeTabId == null && afterTabId == null) {
|
|
1773
|
+
const groupTabs = windowSnapshot.tabs.filter((tab) => tab.groupId === existingGroupId);
|
|
1774
|
+
const indices = groupTabs.map((tab) => normalizeTabIndex2(tab.index)).filter((value) => value != null);
|
|
1775
|
+
if (indices.length) {
|
|
1776
|
+
insertIndex = Math.max(...indices) + 1;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1439
1779
|
let nextIndex = insertIndex;
|
|
1440
1780
|
for (const url of urls) {
|
|
1781
|
+
if (!allowDuplicates && existingGroupId != null) {
|
|
1782
|
+
const norm = normalizeUrl2(url);
|
|
1783
|
+
if (norm && existingUrlSet.has(norm)) {
|
|
1784
|
+
skipped.push({ url, reason: "duplicate" });
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1441
1788
|
try {
|
|
1442
1789
|
const createOptions = { windowId, url, active: false };
|
|
1443
1790
|
if (nextIndex != null) {
|
|
@@ -1461,18 +1808,41 @@
|
|
|
1461
1808
|
try {
|
|
1462
1809
|
const tabIds = created.map((tab) => tab.tabId).filter((id) => typeof id === "number");
|
|
1463
1810
|
if (tabIds.length > 0) {
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1811
|
+
if (existingGroupId != null) {
|
|
1812
|
+
groupId = await chrome.tabs.group({ groupId: existingGroupId, tabIds });
|
|
1813
|
+
} else {
|
|
1814
|
+
groupId = await chrome.tabs.group({ tabIds, createProperties: { windowId } });
|
|
1815
|
+
const update = { title: groupTitle };
|
|
1816
|
+
if (groupColor) {
|
|
1817
|
+
update.color = groupColor;
|
|
1818
|
+
}
|
|
1819
|
+
await chrome.tabGroups.update(groupId, update);
|
|
1468
1820
|
}
|
|
1469
|
-
await chrome.tabGroups.update(groupId, update);
|
|
1470
1821
|
}
|
|
1471
1822
|
} catch (error) {
|
|
1472
1823
|
deps2.log("Failed to create group", error);
|
|
1473
1824
|
groupId = null;
|
|
1474
1825
|
}
|
|
1475
1826
|
}
|
|
1827
|
+
if (groupTitle && created.length === 0 && existingGroupId != null) {
|
|
1828
|
+
groupId = existingGroupId;
|
|
1829
|
+
}
|
|
1830
|
+
try {
|
|
1831
|
+
const freshTabs = await chrome.tabs.query({ windowId });
|
|
1832
|
+
freshTabs.sort((a, b) => a.index - b.index);
|
|
1833
|
+
const firstUngroupedIndex = freshTabs.findIndex((t) => (t.groupId ?? -1) === -1);
|
|
1834
|
+
if (firstUngroupedIndex >= 0) {
|
|
1835
|
+
const groupedAfterUngrouped = freshTabs.filter((t, i) => i > firstUngroupedIndex && (t.groupId ?? -1) !== -1);
|
|
1836
|
+
if (groupedAfterUngrouped.length > 0) {
|
|
1837
|
+
const tabIdsToMove = groupedAfterUngrouped.map((t) => t.id).filter((id) => typeof id === "number");
|
|
1838
|
+
if (tabIdsToMove.length > 0) {
|
|
1839
|
+
await chrome.tabs.move(tabIdsToMove, { index: firstUngroupedIndex });
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
} catch (err) {
|
|
1844
|
+
deps2.log("Failed to reorder groups before ungrouped tabs", err);
|
|
1845
|
+
}
|
|
1476
1846
|
return {
|
|
1477
1847
|
windowId,
|
|
1478
1848
|
groupId,
|
|
@@ -1825,7 +2195,7 @@
|
|
|
1825
2195
|
var content2 = require_content();
|
|
1826
2196
|
var { isScriptableUrl: isScriptableUrl2, isGitHubIssueOrPr: isGitHubIssueOrPr2, detectGitHubState: detectGitHubState2, extractPageMeta: extractPageMeta2, extractSelectorSignal: extractSelectorSignal2, waitForSettle: waitForSettle2, waitForTabReady: waitForTabReady2 } = content2;
|
|
1827
2197
|
var tabs2 = require_tabs();
|
|
1828
|
-
var { normalizeUrl } = tabs2;
|
|
2198
|
+
var { normalizeUrl: normalizeUrl2 } = tabs2;
|
|
1829
2199
|
exports.DEFAULT_STALE_DAYS = 30;
|
|
1830
2200
|
exports.DESCRIPTION_MAX_LENGTH = 250;
|
|
1831
2201
|
exports.SELECTOR_VALUE_MAX_LENGTH = 500;
|
|
@@ -1852,7 +2222,7 @@
|
|
|
1852
2222
|
const normalizedMap = /* @__PURE__ */ new Map();
|
|
1853
2223
|
const duplicates = /* @__PURE__ */ new Map();
|
|
1854
2224
|
for (const tab of scopeTabs) {
|
|
1855
|
-
const normalized =
|
|
2225
|
+
const normalized = normalizeUrl2(tab.url);
|
|
1856
2226
|
if (!normalized) {
|
|
1857
2227
|
continue;
|
|
1858
2228
|
}
|
|
@@ -2119,6 +2489,7 @@
|
|
|
2119
2489
|
exports.undoGroupUpdate = undoGroupUpdate;
|
|
2120
2490
|
exports.undoGroupUngroup = undoGroupUngroup;
|
|
2121
2491
|
exports.undoGroupAssign = undoGroupAssign;
|
|
2492
|
+
exports.undoGroupGather = undoGroupGather;
|
|
2122
2493
|
exports.undoMoveTab = undoMoveTab;
|
|
2123
2494
|
exports.undoMoveGroup = undoMoveGroup;
|
|
2124
2495
|
exports.undoMergeWindow = undoMergeWindow;
|
|
@@ -2303,6 +2674,10 @@
|
|
|
2303
2674
|
const tabs2 = undo.tabs || [];
|
|
2304
2675
|
return await restoreTabsFromUndo(tabs2, deps2);
|
|
2305
2676
|
}
|
|
2677
|
+
async function undoGroupGather(undo, deps2) {
|
|
2678
|
+
const tabs2 = undo.tabs || [];
|
|
2679
|
+
return await restoreTabsFromUndo(tabs2, deps2);
|
|
2680
|
+
}
|
|
2306
2681
|
async function undoMoveTab(undo, deps2) {
|
|
2307
2682
|
const from = undo.from || {};
|
|
2308
2683
|
const entry = {
|
|
@@ -2515,6 +2890,9 @@
|
|
|
2515
2890
|
if (undo.action === "group-assign") {
|
|
2516
2891
|
return await undoGroupAssign(undo, deps2);
|
|
2517
2892
|
}
|
|
2893
|
+
if (undo.action === "group-gather") {
|
|
2894
|
+
return await undoGroupGather(undo, deps2);
|
|
2895
|
+
}
|
|
2518
2896
|
if (undo.action === "move-tab") {
|
|
2519
2897
|
return await undoMoveTab(undo, deps2);
|
|
2520
2898
|
}
|
|
@@ -3149,6 +3527,8 @@
|
|
|
3149
3527
|
return await groupUngroup(params);
|
|
3150
3528
|
case "group-assign":
|
|
3151
3529
|
return await groupAssign(params);
|
|
3530
|
+
case "group-gather":
|
|
3531
|
+
return await groupGather(params);
|
|
3152
3532
|
case "move-tab":
|
|
3153
3533
|
return await move.moveTab(params, deps);
|
|
3154
3534
|
case "move-group":
|
|
@@ -3165,6 +3545,9 @@
|
|
|
3165
3545
|
return await screenshot.screenshotTabs(params, requestId, deps);
|
|
3166
3546
|
case "undo":
|
|
3167
3547
|
return await undoHandlers.undoTransaction(params, deps);
|
|
3548
|
+
case "reload":
|
|
3549
|
+
setTimeout(() => chrome.runtime.reload(), 100);
|
|
3550
|
+
return { reloading: true };
|
|
3168
3551
|
default:
|
|
3169
3552
|
throw new Error(`Unknown action: ${action}`);
|
|
3170
3553
|
}
|
|
@@ -3317,6 +3700,9 @@
|
|
|
3317
3700
|
async function groupAssign(params) {
|
|
3318
3701
|
return groups.groupAssign(params, deps);
|
|
3319
3702
|
}
|
|
3703
|
+
async function groupGather(params) {
|
|
3704
|
+
return groups.groupGather(params, deps);
|
|
3705
|
+
}
|
|
3320
3706
|
async function getArchiveWindowId() {
|
|
3321
3707
|
await ensureArchiveWindowIdLoaded();
|
|
3322
3708
|
return state.archiveWindowId;
|