diff --git a/src/main.ts b/src/main.ts index 4fe4e22..11698d0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -67,6 +67,17 @@ export default class OpenTabSettingsPlugin extends Plugin { }); }); } + const activeLeaf = this.app.workspace.getMostRecentLeaf(); + if (activeLeaf && this.getAllTabGroups(activeLeaf.getRoot()).length > 1) { + menu.addItem((item) => { + item.setSection("open"); + item.setIcon("lucide-split-square-horizontal") + item.setTitle("Open in opposite tab group"); + item.onClick(async () => { + await this.app.workspace.getLeaf('opposite' as PaneType).openFile(file); + }); + }); + } } }) ); @@ -123,19 +134,23 @@ export default class OpenTabSettingsPlugin extends Plugin { return function(this: Workspace, newLeaf?: PaneTypePatch|boolean, ...args) { const activeLeaf = this.getActiveViewOfType(View)?.leaf; - let implicitOpen = !newLeaf || newLeaf == "allow-duplicate"; - const allowDuplicate = newLeaf == "allow-duplicate"; + let origNewLeaf = newLeaf; // resolve newLeaf to enum if (newLeaf == true) { newLeaf = 'tab'; } else if (!newLeaf || newLeaf == "allow-duplicate") { newLeaf = plugin.settings.openInNewTab ? 'tab' : 'same'; + } else if (newLeaf == "opposite") { + newLeaf = 'tab'; } let leaf: WorkspaceLeaf; if (newLeaf == 'tab') { // Tabs opened via normal click are always focused regardless of focusNewTab setting. - leaf = plugin.createNewLeaf(implicitOpen ? true : undefined); + leaf = plugin.createNewLeaf( + !origNewLeaf || origNewLeaf == "allow-duplicate" ? true : undefined, + origNewLeaf == "opposite" ? "opposite" : undefined, + ); } else if (newLeaf == "same") { leaf = plugin.getUnpinnedLeaf(); } else { @@ -144,7 +159,9 @@ export default class OpenTabSettingsPlugin extends Plugin { // we set these to be used in openFile so we can tell when to deduplicate files. leaf.openTabSettings = { - openType: newLeaf, implicitOpen, allowDuplicate, + openType: newLeaf, + implicitOpen: !origNewLeaf, + allowDuplicate: origNewLeaf == "allow-duplicate", openedFrom: activeLeaf?.id, } @@ -313,10 +330,13 @@ export default class OpenTabSettingsPlugin extends Plugin { * Custom variant of the internal workspace.createLeafInTabGroup function that follows our new tab placement logic. * @param focus Whether to focus the new tab. If undefined focus based on focusNewTab config */ - private createNewLeaf(focus?: boolean) { + private createNewLeaf( + focus?: boolean, newTabTabGroupPlacement?: OpenTabSettingsPluginSettings["newTabTabGroupPlacement"], + ) { const plugin = this; const workspace = plugin.app.workspace; focus = focus ?? plugin.app.vault.getConfig('focusNewTab') as boolean; + newTabTabGroupPlacement = newTabTabGroupPlacement ?? plugin.settings.newTabTabGroupPlacement; const activeLeaf = workspace.getMostRecentLeaf(); if (!activeLeaf) throw new Error("No tab group found."); @@ -331,14 +351,14 @@ export default class OpenTabSettingsPlugin extends Plugin { let group: TabGroup|undefined; let index: number|undefined; - if (plugin.settings.newTabTabGroupPlacement != "same" && !Platform.isPhone) { + if (newTabTabGroupPlacement != "same" && !Platform.isPhone) { const tabGroups = plugin.getAllTabGroups(activeLeaf.getRoot()); const otherTabGroup = tabGroups.filter(g => g !== activeTabGroup).at(-1); - if (plugin.settings.newTabTabGroupPlacement == "opposite" && otherTabGroup) { + if (newTabTabGroupPlacement == "opposite" && otherTabGroup) { group = otherTabGroup; - } else if (plugin.settings.newTabTabGroupPlacement == "first" && tabGroups.at(0)) { + } else if (newTabTabGroupPlacement == "first" && tabGroups.at(0)) { group = tabGroups[0]; - } else if (plugin.settings.newTabTabGroupPlacement == "last" && tabGroups.at(-1)) { + } else if (newTabTabGroupPlacement == "last" && tabGroups.at(-1)) { group = tabGroups.at(-1)!; } } diff --git a/src/settings.ts b/src/settings.ts index c34cdaa..be0fffd 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -19,6 +19,7 @@ export const MOD_CLICK_BEHAVIOR = { "tab": "In new tab", "same": "In same tab", "allow-duplicate": "In duplicate tab", + "opposite": "In opposite tab group", } export interface OpenTabSettingsPluginSettings { @@ -90,6 +91,7 @@ export class OpenTabSettingsPluginSettingTab extends PluginSettingTab { if (this.plugin.settings.deduplicateTabs) { dropdown.addOption("allow-duplicate", MOD_CLICK_BEHAVIOR['allow-duplicate']) } + dropdown.addOption("opposite", MOD_CLICK_BEHAVIOR['opposite']) dropdown .setValue(this.plugin.settings.modClickBehavior) .onChange(async value => { diff --git a/src/types.d.ts b/src/types.d.ts index eda386f..886933d 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,6 +1,6 @@ import { PaneType, WorkspaceTabs, WorkspaceMobileDrawer } from 'obsidian'; -export type PaneTypePatch = PaneType|"same"|"allow-duplicate"; +export type PaneTypePatch = PaneType|"same"|"allow-duplicate"|"opposite"; declare module "obsidian" { interface WorkspaceLeaf { diff --git a/test/specs/misc.e2e.ts b/test/specs/misc.e2e.ts index 63c13c6..dadc9b0 100644 --- a/test/specs/misc.e2e.ts +++ b/test/specs/misc.e2e.ts @@ -53,6 +53,22 @@ describe('Misc', function() { }); expect(value).toEqual(true); }); + + it("opposite menu item", async function() { + await workspacePage.setSettings({ + openInNewTab: true, deduplicateTabs: false, newTabTabGroupPlacement: "same", + }); + + await workspacePage.openFile("A.md"); + await workspacePage.openLinkToRight(await workspacePage.getLink("B")); + await workspacePage.setActiveFile("A.md"); + + await workspacePage.openLinkMenuOption(await workspacePage.getLink("B"), "Open in opposite tab group"); + await workspacePage.matchWorkspace([ + [{type: "markdown", file: "A.md"}], + [{type: "markdown", file: "B.md"}, {type: "markdown", file: "B.md"}] + ]); + }) }) describe("Mod click", function() { @@ -97,4 +113,20 @@ describe("Mod click", function() { {type: "markdown", file: "B.md", active: true}, ]]); }); + + it("mode click opposite", async function() { + await workspacePage.setSettings({ + openInNewTab: true, deduplicateTabs: false, newTabTabGroupPlacement: "opposite", + }); + + await workspacePage.openFile("A.md"); + await workspacePage.openLinkToRight(await workspacePage.getLink("B")); + await workspacePage.setActiveFile("A.md"); + + await (await workspacePage.getLink("B")).click({"button": "middle"}); + await workspacePage.matchWorkspace([ + [{type: "markdown", file: "A.md"}], + [{type: "markdown", file: "B.md"}, {type: "markdown", file: "B.md"}] + ]); + }) })