webhelm 1.0.23 → 1.0.25
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
CHANGED
|
@@ -39,7 +39,7 @@ The daemon-managed Chrome Extension is installed from the npm Runtime and does n
|
|
|
39
39
|
|
|
40
40
|
The compact popup shows daemon connectivity and per-workspace capacity and Session counts, with confirmation before stopping a Session or clearing Sessions in one workspace. The popup does not include a global stop-all action. These actions are executed by the same Node daemon as the CLI, over the existing CDP-backed Extension RPC. No shell access or HTTP listener is granted to the Extension. The persistent ChatGPT queue is not deleted, and queued work may create new Sessions afterward.
|
|
41
41
|
|
|
42
|
-
New installations use the default Chrome tab group `WebHelm` and the idle page `helm.html`. Idle URLs use only `workspace` and `role=idle`; Lease URLs use Chrome's actual `tabId` for initial CDP target discovery. There is no separate slot identity.
|
|
42
|
+
New installations use the default Chrome tab group `WebHelm` and the idle page `helm.html`. Idle URLs use only `workspace` and `role=idle`; Lease URLs use Chrome's actual `tabId` for initial CDP target discovery. There is no separate slot identity. Every newly created WebHelm-managed tab group (default and custom) is placed at the far left of its Chrome window, immediately after pinned tabs. Existing groups retain their user-chosen position during restoration, legacy migration and Session allocation. Chrome-saved tab group shortcuts in the bookmarks bar are separate from active groups and cannot safely be deduplicated through the Extension API. Legacy `Harness` default groups migrate only when their tabs are idle. Custom workspace names are unchanged; the old `workspace.html` path and lease titles remain readable until their Sessions finish. Closing an allocated Session returns its managed tabs to the idle page, retaining physical Chrome targets, while unleased popup children are closed. Do not manually remove active managed tabs.
|
|
43
43
|
|
|
44
44
|
For a real browser integration check on macOS, run `node test/e2e/extension-control.e2e.mjs`; it creates a temporary isolated Chrome profile and daemon and does not touch the current production Sessions.
|
|
45
45
|
|
package/package.json
CHANGED
|
@@ -242,20 +242,14 @@ export class WorkspaceManager {
|
|
|
242
242
|
return await this.chrome.tabs.query({ groupId });
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
async positionDefaultGroupFirst(groupId, name) {
|
|
249
|
-
if (name !== "WebHelm") return false;
|
|
245
|
+
// New groups belong immediately after pinned tabs, regardless of Chrome's
|
|
246
|
+
// insertion policy. Existing groups keep their order on restore and lease.
|
|
247
|
+
async positionNewGroupLeftmost(groupId) {
|
|
250
248
|
const groupTabs = await this.groupTabs(groupId);
|
|
251
|
-
if (!groupTabs.length) return
|
|
252
|
-
const
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
const firstGroupIndex = Math.min(...groupTabs.map(tab => tab.index));
|
|
256
|
-
if (firstGroupIndex === firstUnpinnedIndex) return false;
|
|
257
|
-
await this.chrome.tabGroups.move(groupId, {index: firstUnpinnedIndex});
|
|
258
|
-
return true;
|
|
249
|
+
if (!groupTabs.length) return;
|
|
250
|
+
const windowTabs = await this.chrome.tabs.query({ windowId: groupTabs[0].windowId });
|
|
251
|
+
const firstUnpinned = windowTabs.filter(tab => tab.pinned).length;
|
|
252
|
+
await this.chrome.tabGroups.move(groupId, { index: firstUnpinned });
|
|
259
253
|
}
|
|
260
254
|
|
|
261
255
|
tabUrl(tab) {
|
|
@@ -507,7 +501,7 @@ export class WorkspaceManager {
|
|
|
507
501
|
for (const tab of created) this.markOwned(tab.id);
|
|
508
502
|
const groupId = await this.chrome.tabs.group({ tabIds: created.map((tab) => tab.id) });
|
|
509
503
|
await this.chrome.tabGroups.update(groupId, { title: name, collapsed: true });
|
|
510
|
-
await this.
|
|
504
|
+
await this.positionNewGroupLeftmost(groupId);
|
|
511
505
|
for (const tab of created) await this.waitForIdleTab(tab.id, name);
|
|
512
506
|
return await this.status(name);
|
|
513
507
|
}
|
|
@@ -586,8 +580,6 @@ export class WorkspaceManager {
|
|
|
586
580
|
for (const name of Object.keys(config.workspaces || {}).sort()) {
|
|
587
581
|
restored.push(await this.ensureWorkspace(name));
|
|
588
582
|
}
|
|
589
|
-
const defaultGroup = restored.find(item => item.name === "WebHelm" && item.initialized);
|
|
590
|
-
if (defaultGroup) await this.positionDefaultGroupFirst(defaultGroup.groupId, "WebHelm");
|
|
591
583
|
return { workspaces: restored };
|
|
592
584
|
}
|
|
593
585
|
|
|
@@ -625,9 +617,6 @@ export class WorkspaceManager {
|
|
|
625
617
|
await this.chrome.tabs.update(tab.id, {url: this.idleUrl(newName), active: false});
|
|
626
618
|
updated.push(tab);
|
|
627
619
|
}
|
|
628
|
-
// Position before committing the new config so move failures still
|
|
629
|
-
// unwind the legacy migration coherently in the catch block.
|
|
630
|
-
if (group) await this.positionDefaultGroupFirst(group.id, newName);
|
|
631
620
|
config.workspaces[newName] = config.workspaces[oldName];
|
|
632
621
|
delete config.workspaces[oldName];
|
|
633
622
|
await this.saveConfig(config);
|