fix: headed viewport auto keeps the user's active tab; render's closetab opts out of sessions

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-11-01 00:18:59 +00:00
1 parent 48bba66b16
commit 59bed0f317
4 files changed
+28 -7

No files matched your search

+8 -1
View File
@@ -1811,9 +1811,16 @@ export class BrowserManager {
const pinned = this.getPage();
const loaded = this.getActiveSession().getLoadedHtml();
const url = pinned.url();
const activeBefore = this.activeTabId;
const page = await this.context.newPage();
const listenerId = [...this.pages].find(([, p]) => p === page)?.[0];
if (listenerId !== undefined) {
this.pages.delete(listenerId);
this.tabSessions.delete(listenerId);
}
this.activeTabId = activeBefore;
const session = new TabSession(page);
this.wirePageEvents(page);
if (listenerId === undefined) this.wirePageEvents(page);
try {
if (loaded) await session.setTabContent(loaded.html, { waitUntil: loaded.waitUntil });
else if (url !== 'about:blank') await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 15000 });
+4 -1
View File
@@ -119,6 +119,9 @@ describe('CLI retry after a token change', () => {
describe('gstack-render browse calls', () => {
test('turn the automatic session off: render pins its own tab ids', () => {
const source = fs.readFileSync(path.resolve(import.meta.dir, '../../lib/aside-render.ts'), 'utf8');
expect(source).toMatch(/runProc\(bin, args, [^\n]*BROWSE_SESSION: ''/);
expect(source).toContain("const browseEnv = { ...process.env, BROWSE_SESSION: '' };");
const calls = source.match(/runProc\(bin,[^\n]*/g) ?? [];
expect(calls.length).toBeGreaterThanOrEqual(2);
for (const call of calls) expect(call).toContain('browseEnv');
});
});
+13 -3
View File
@@ -225,11 +225,20 @@ const rootTab = bm.getActiveTabId();
const pinned = JSON.parse((await run('newtab', [U('vp'), '--json'])).result).tabId;
bm.switchTab(userTab, { bringToFront: false });
const mode = bm.connectionMode;
const listener = (page) => {
const id = bm.nextTabId++;
bm.pages.set(id, page);
bm.tabSessions.set(id, new (bm.tabSessions.values().next().value.constructor)(page));
bm.activeTabId = id;
};
bm.context.on('page', listener);
bm.connectionMode = 'headed';
let r;
try { r = await run('viewport', ['auto'], { tabId: pinned }); } finally { bm.connectionMode = mode; }
try { r = await run('viewport', ['auto'], { tabId: pinned }); } finally { bm.connectionMode = mode; bm.context.off('page', listener); }
const pinnedPage = bm.pages.get(pinned);
steps.headedViewport = { status: r.status, userTracked: bm.pages.get(userTab) === userPage,
pinnedLive: bm.pages.has(pinned), pinnedUrl: bm.pages.get(pinned)?.url() ?? null };
pinnedLive: bm.pages.has(pinned), pinnedUrl: pinnedPage?.url() ?? null, activeKept: bm.getActiveTabId() === userTab,
sameObject: [...bm.pages.values()].filter(p => p === pinnedPage).length, rootUrl: (await run('url')).result };
}
{ // state save and load inside a session land the session on its own saved page
@@ -364,7 +373,8 @@ describe('session lifecycle', () => {
expect(steps.headedNew).toEqual({ activeKept: true, added: 1, sameObject: 1 });
});
test('headed: viewport auto under a tab pin rebuilds only the pinned tab', () => {
expect(steps.headedViewport).toMatchObject({ status: 200, userTracked: true, pinnedLive: true });
expect(steps.headedViewport).toMatchObject({ status: 200, userTracked: true, pinnedLive: true, activeKept: true, sameObject: 1 });
expect(steps.headedViewport.rootUrl).not.toContain('s=vp');
expect(steps.headedViewport.pinnedUrl).toContain('s=vp');
});
test('state save and load inside a session leave it on its saved page', () => {
+3 -2
View File
@@ -526,8 +526,9 @@ export async function renderWithBrowse(spec: RenderSpec, bin: string | null = re
if (target.kind === 'error') return fail(target.error);
const deadline = Date.now() + (spec.timeoutMs ?? DEFAULT_TIMEOUT_MS);
const browseEnv = { ...process.env, BROWSE_SESSION: '' };
const run = async (args: string[]): Promise<string> => {
const r = await runProc(bin, args, Math.max(1_000, Math.min(120_000, deadline - Date.now())), { ...process.env, BROWSE_SESSION: '' });
const r = await runProc(bin, args, Math.max(1_000, Math.min(120_000, deadline - Date.now())), browseEnv);
log.push(`$ browse ${args.join(' ').slice(0, 300)}\n${r.stdout}${r.stderr}`.trim());
if (r.error || r.code !== 0) {
const first = (r.stderr || r.stdout || r.error || '').trim().split('\n')[0];
@@ -644,7 +645,7 @@ export async function renderWithBrowse(spec: RenderSpec, bin: string | null = re
} catch (e) {
return fail((e as Error).message);
} finally {
if (tab !== undefined) await runProc(bin, ['closetab', String(tab)], 15_000);
if (tab !== undefined) await runProc(bin, ['closetab', String(tab)], 15_000, browseEnv);
srv?.stop();
if (work) fs.rmSync(work, { recursive: true, force: true });
}