From fb7d2feeef84ac6ab026b2147f52522d17fe40a2 Mon Sep 17 00:00:00 2001 From: Oliver Blanthorn Date: Fri, 31 Jul 2026 21:38:25 +0200 Subject: [PATCH] Fix #5493: add :splitnext --- src/excmds.test.ts | 21 +++++++++++++++++++++ src/excmds.ts | 10 ++++++++++ src/lib/config.ts | 1 + src/tridactyl.d.ts | 6 ++++++ 4 files changed, 38 insertions(+) diff --git a/src/excmds.test.ts b/src/excmds.test.ts index 4ffce028..b4c6a982 100644 --- a/src/excmds.test.ts +++ b/src/excmds.test.ts @@ -217,6 +217,27 @@ test("`changelistjump` skips closed tabs", async () => { ]) }) +test.each([ + [7, [[2, { active: true }]]], + [-1, []], + [undefined, []], +])( + "`splitnext` handles split view ID %s", + async (splitViewId, expectedCalls) => { + const query = jest.mocked(browser.tabs.query) + const update = jest.mocked(browser.tabs.update) + update.mockClear() + query.mockResolvedValueOnce([ + { id: 1, active: true, splitViewId }, + { id: 2, active: false, splitViewId }, + ] as browser.tabs.Tab[]) + + await backgroundExcmds.splitnext() + expect(query).toHaveBeenLastCalledWith({ currentWindow: true }) + expect(update.mock.calls).toEqual(expectedCalls) + }, +) + test("`nativeopen` targets the running macOS Firefox application", async () => { jest.mocked(browser.runtime.getPlatformInfo).mockResolvedValue({ arch: "x86-64", diff --git a/src/excmds.ts b/src/excmds.ts index 35e72cfd..42358496 100644 --- a/src/excmds.ts +++ b/src/excmds.ts @@ -2739,6 +2739,16 @@ async function tabIndexSetActive(index: number | string) { return tabSetActive(await idFromIndex(index)) } +/** Switch focus to the other side of the current split view, if it exists */ +//#background +export async function splitnext() { + const tabs = await browser.tabs.query({ currentWindow: true }) + const splitViewId = tabs.find(tab => tab.active)?.splitViewId + if (splitViewId === undefined || splitViewId < 0) return + const next = tabs.find(tab => !tab.active && tab.splitViewId === splitViewId) + if (next) return tabSetActive(next.id) +} + /** Switch to the first tab in Firefox's tab order. */ //#background export async function tabfirst() { diff --git a/src/lib/config.ts b/src/lib/config.ts index 22bdd4f1..173def7d 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -325,6 +325,7 @@ export class default_config { K: "tabnext", gt: "tabnext_gt", gT: "tabprev", + gs: "splitnext", // c-w w is reserved for closing windows // "": "tabnext_gt", // c-n is reserved for new window // "": "tabprev", "g^": "tabfirst", diff --git a/src/tridactyl.d.ts b/src/tridactyl.d.ts index 9ef2dbd4..20d23ba6 100644 --- a/src/tridactyl.d.ts +++ b/src/tridactyl.d.ts @@ -20,6 +20,12 @@ interface HTMLElement { openOrClosedShadowRoot: ShadowRoot | null } +declare namespace browser.tabs { + interface Tab { + splitViewId?: number + } +} + /* eslint-disable @typescript-eslint/no-unsafe-function-type */ // these functions really can be anything, ditto for the objects declare function exportFunction(