### Plugin Settings Configuration Example Source: https://context7.com/jesse-r-s-hines/obsidian-open-tab-settings/llms.txt Demonstrates how to define and update settings for the Open Tab Settings plugin. Settings include options for opening files in new tabs, deduplicating tabs, and customizing tab placement. These settings are persisted to disk. ```typescript import OpenTabSettingsPlugin from './main'; import { OpenTabSettingsPluginSettings, DEFAULT_SETTINGS } from './settings'; // Default settings structure const DEFAULT_SETTINGS: OpenTabSettingsPluginSettings = { openInNewTab: true, // Open files in new tabs by default deduplicateTabs: true, // Switch to existing tabs instead of duplicating openInSameTabOnModClick: false, // Ctrl/middle-click opens in same tab newTabPlacement: "after-active", // Where to place new tabs newTabTabGroupPlacement: "same", // Which pane to use in split view }; // Update settings programmatically class ExamplePlugin extends Plugin { settings: OpenTabSettingsPluginSettings; async updateTabSettings() { // Load current settings await this.loadSettings(); // Update specific settings await this.updateSettings({ openInNewTab: true, deduplicateTabs: false }); // Settings are automatically persisted to data.json console.log('Settings updated:', this.settings); } } ``` -------------------------------- ### Example: Demonstrating Tab Deduplication Behavior (TypeScript) Source: https://context7.com/jesse-r-s-hines/obsidian-open-tab-settings/llms.txt This example demonstrates the tab deduplication functionality. The first `openFile` call for a given file creates a new tab. The second `openFile` call for the same file, with deduplication enabled, switches focus to the existing tab instead of creating a duplicate. ```typescript // Example: Deduplication behavior const file = app.vault.getAbstractFileByPath('existing.md'); // First open - creates new tab const leaf1 = app.workspace.getLeaf('tab'); await leaf1.openFile(file); // Opens in new tab // Second open with deduplication enabled - switches to existing const leaf2 = app.workspace.getLeaf('tab'); await leaf2.openFile(file); // Switches to leaf1 instead of creating duplicate ``` -------------------------------- ### Update Plugin Settings for Tab Placement (JavaScript) Source: https://context7.com/jesse-r-s-hines/obsidian-open-tab-settings/llms.txt These JavaScript examples demonstrate how to interact with the Obsidian Open Tab Settings plugin to modify its configuration for new tab placement. The `updateSettings` method is used to change properties like `newTabPlacement` and `newTabTabGroupPlacement`. These changes affect how subsequent calls to `app.workspace.getLeaf('tab')` will position the new tab. ```javascript // Example: Different placement configurations const plugin = app.plugins.getPlugin('open-tab-settings'); // Place new tabs at the end of tab bar await plugin.updateSettings({ newTabPlacement: "end" }); const leaf1 = app.workspace.getLeaf('tab'); // Opens at end // Place new tabs after active tab (default) await plugin.updateSettings({ newTabPlacement: "after-active" }); const leaf2 = app.workspace.getLeaf('tab'); // Opens next to current // Open new tabs in opposite split pane await plugin.updateSettings({ newTabTabGroupPlacement: "opposite" }); const leaf3 = app.workspace.getLeaf('tab'); // Opens in other pane ``` -------------------------------- ### Workspace.getLeaf Monkey Patch for Tab Control Source: https://context7.com/jesse-r-s-hines/obsidian-open-tab-settings/llms.txt Illustrates the core functionality of the plugin, which involves monkey-patching Obsidian's `Workspace.getLeaf()` method. This patch intercepts tab creation requests to apply custom logic based on plugin settings, such as opening files in new tabs, preventing duplicates, or controlling placement. ```typescript import * as monkeyAround from 'monkey-around'; import { Workspace, WorkspaceLeaf, PaneType } from 'obsidian'; // Register the monkey patch this.register(monkeyAround.around(Workspace.prototype, { getLeaf(oldMethod: any) { return function(this: Workspace, newLeaf?: PaneType|boolean, ...args) { const plugin = this; // Reference to OpenTabSettingsPlugin const activeLeaf = plugin.app.workspace.getActiveViewOfType(View)?.leaf; // Determine if this is a default new tab request const isDefaultNewTab = plugin.settings.openInNewTab && !newLeaf; // Resolve newLeaf to enum based on settings let leaf: WorkspaceLeaf; if (newLeaf == 'tab' || (plugin.settings.openInNewTab && !newLeaf)) { // Create new tab using custom placement logic leaf = plugin.getNewLeaf(); // Focus new tab if configured or default click if (plugin.app.vault.getConfig('focusNewTab') || isDefaultNewTab) { plugin.app.workspace.setActiveLeaf(leaf); } } else { // Use Obsidian's default behavior leaf = oldMethod.call(this, newLeaf, ...args); } // Track metadata for deduplication logic leaf.openTabSettingsLastOpenType = newLeaf || 'default'; leaf.openTabSettingsOpenedFrom = activeLeaf?.id; return leaf; } } })); // Example: Opening a file with different behaviors const file = app.vault.getAbstractFileByPath('note.md'); // Open in new tab (when openInNewTab is true) const newTabLeaf = app.workspace.getLeaf('tab'); await newTabLeaf.openFile(file); // Open in same tab (override default behavior) const sameTabLeaf = app.workspace.getLeaf('same'); await sameTabLeaf.openFile(file); // Open to the right const rightLeaf = app.workspace.getLeaf('split'); await rightLeaf.openFile(file); ``` -------------------------------- ### Custom New Tab Placement Logic (TypeScript) Source: https://context7.com/jesse-r-s-hines/obsidian-open-tab-settings/llms.txt The `getNewLeaf()` method within the `OpenTabSettingsPlugin` class provides custom logic for determining where new tabs should be created. It considers various settings for tab group placement (e.g., 'same', 'opposite', 'first', 'last' in split panes) and tab position within a group (e.g., 'after-pinned', 'beginning', 'end', 'after-active'). The method aims to reuse existing empty tabs when possible before creating new ones. It relies on internal Obsidian API elements like `WorkspaceLeaf`, `TabGroup`, `Platform`, and helper functions like `isEmptyLeaf`. ```typescript class OpenTabSettingsPlugin extends Plugin { private getNewLeaf(): WorkspaceLeaf { const activeLeaf = this.app.workspace.getMostRecentLeaf(); if (!activeLeaf) throw new Error("No tab group found."); const activeTabGroup = activeLeaf.parent; const activeIndex = activeTabGroup.children.indexOf(activeLeaf); // Reuse empty tabs instead of creating new ones if (isEmptyLeaf(activeLeaf)) { return activeLeaf; } let group: TabGroup|undefined; let index: number|undefined; // Determine which tab group to use in split view if (this.settings.newTabTabGroupPlacement != "same" && !Platform.isPhone) { const tabGroups = this.getAllTabGroups(activeLeaf.getRoot()); const otherTabGroup = tabGroups.filter(g => g !== activeTabGroup).at(-1); switch (this.settings.newTabTabGroupPlacement) { case "opposite": group = otherTabGroup || activeTabGroup; break; case "first": group = tabGroups[0]; break; case "last": group = tabGroups.at(-1)!; break; } } group = group || activeTabGroup; // Determine index within the tab group if (group == activeTabGroup) { switch (this.settings.newTabPlacement) { case "after-pinned": // Place after pinned tabs const nextUnpinned = group.children.findIndex((l, i) => !l.pinned && i > activeIndex ); index = nextUnpinned < 0 ? group.children.length : nextUnpinned; break; case "beginning": index = 0; break; case "end": index = group.children.length; break; default: // "after-active" index = activeIndex + 1; } } else { index = this.settings.newTabPlacement == "beginning" ? 0 : group.children.length; } // Reuse empty tab at target position if available const leafToDisplace = group.children[Math.min(index, group.children.length - 1)]; if (isEmptyLeaf(leafToDisplace)) { return leafToDisplace; } // Create new leaf and insert at calculated position const newLeaf = new (WorkspaceLeaf as any)(this.app); group.insertChild(index, newLeaf); return newLeaf; } } ``` -------------------------------- ### Find Matching Workspace Leaves - TypeScript Source: https://context7.com/jesse-r-s-hines/obsidian-open-tab-settings/llms.txt Implements the `findMatchingLeaves` method to locate all `WorkspaceLeaf` instances currently displaying a given `TFile`. It filters leaves based on file path, view type compatibility, and ensures they are in the main workspace area, excluding sidebars. ```typescript class OpenTabSettingsPlugin extends Plugin { private findMatchingLeaves(file: TFile): WorkspaceLeaf[] { const matches: WorkspaceLeaf[] = []; this.app.workspace.iterateAllLeaves(leaf => { // Check if file path matches const isFileMatch = leaf.getViewState()?.state?.file == file.path; // Only match basic file views, not auxiliary views like outgoing-links const viewType = leaf.view.getViewType(); const isTypeMatch = ( this.app.viewRegistry.getTypeByExtension(file.extension) == viewType || PLUGIN_VIEW_TYPES[file.extension]?.includes(viewType) ); // Only consider tabs in main area (not sidebars) if (isMainLeaf(leaf) && isFileMatch && isTypeMatch) { matches.push(leaf); } }); return matches; } } // Example: Finding and managing duplicate tabs const file = app.vault.getAbstractFileByPath('note.md'); const plugin = app.plugins.getPlugin('open-tab-settings'); const matches = plugin.findMatchingLeaves(file); console.log(`File is open in ${matches.length} tabs`); // Close all but first tab for (let i = 1; i < matches.length; i++) { matches[i].detach(); } // Or switch to first existing tab if (matches.length > 0) { app.workspace.setActiveLeaf(matches[0]); } ``` -------------------------------- ### Intercept File Opening to Prevent Duplicate Tabs (TypeScript) Source: https://context7.com/jesse-r-s-hines/obsidian-open-tab-settings/llms.txt This TypeScript code patches the `WorkspaceLeaf.prototype.openFile` method to enable tab deduplication. It checks if the file is already open and, if so, switches to the existing tab instead of creating a new one. This functionality is controlled by plugin settings and considers different opening contexts, such as internal link navigation. ```typescript import { WorkspaceLeaf, TFile } from 'obsidian'; import * as monkeyAround from 'monkey-around'; this.register(monkeyAround.around(WorkspaceLeaf.prototype, { openFile(oldMethod: any) { return async function(this: WorkspaceLeaf, file: TFile, openState, ...args) { const plugin = this; // Reference to OpenTabSettingsPlugin // Find all leaves displaying this file const matches = plugin.findMatchingLeaves(file); const lastOpenType = this.openTabSettingsLastOpenType; const lastOpenedFrom = this.openTabSettingsOpenedFrom; // Check if this is a special open (new window, sidebar, etc.) const isSpecialOpen = (!isMainLeaf(this) || ( isEmptyLeaf(this) && !['same', 'tab', 'default'].includes(lastOpenType ?? 'unknown') )); // Detect internal link navigation (heading/block links) const isInternalLink = ( isEmptyLeaf(this) && !!openState?.eState?.subpath && matches.some(l => l.id == lastOpenedFrom) ); let match: WorkspaceLeaf|undefined; if (plugin.settings.openInNewTab && isInternalLink && !isSpecialOpen) { // Internal links always deduplicate to parent tab match = matches.find(l => l.id == lastOpenedFrom); } else if (plugin.settings.deduplicateTabs && !isSpecialOpen && matches.length > 0 && !matches.includes(this)) { // Switch to first existing tab match = matches[0]; } if (match) { // Reuse existing tab instead of opening duplicate await oldMethod.call(match, file, { ...openState, active: !!openState?.active || activeLeaf == this, }, ...args); // Close empty leaf that was going to be used if (isEmptyLeaf(this) && this.parent.children.length > 1) { this.detach(); } } else { // Open normally await oldMethod.call(this, file, openState, ...args); } } } })); ``` -------------------------------- ### Add 'Open in Same Tab' Context Menu Item - TypeScript Source: https://context7.com/jesse-r-s-hines/obsidian-open-tab-settings/llms.txt Adds a context menu item to file menus that allows users to explicitly open a file in the current tab. This overrides the 'always open in new tab' setting for that specific action. It uses the `file-menu` event and `registerEvent` API. ```typescript class OpenTabSettingsPlugin extends Plugin { async onload() { this.registerEvent( this.app.workspace.on("file-menu", (menu, file, source, leaf) => { if (file instanceof TFile) { menu.addItem((item) => { item.setSection("open"); item.setIcon("file-minus"); item.setTitle("Open in same tab"); item.onClick(async () => { // Force open in same tab, bypassing openInNewTab setting await this.app.workspace.getLeaf('same' as PaneType) .openFile(file); }); }); } }) ); } } // Example: Context menu usage // Right-click on any file in file explorer or tab header // Select "Open in same tab" to open without creating new tab // This works even when "Always open in new tab" setting is enabled ``` -------------------------------- ### Register Commands for Tab Settings - TypeScript Source: https://context7.com/jesse-r-s-hines/obsidian-open-tab-settings/llms.txt Registers toggle, enable, and disable commands for 'always open in new tab' and 'prevent duplicate tabs' settings. These commands can be invoked via the command palette or hotkeys. They update the plugin's settings based on user interaction. ```typescript class OpenTabSettingsPlugin extends Plugin { async onload() { const commands = [ ["openInNewTab", "always open in new tab"], ["deduplicateTabs", "prevent duplicate tabs"], ] as const; for (const [setting, name] of commands) { const id = setting.replace(/[A-Z]/g, l => `-${l.toLowerCase()}`); // Toggle command this.addCommand({ id: `toggle-${id}`, name: `Toggle ${name}`, callback: async () => { await this.updateSettings({ [setting]: !this.settings[setting] }); }, }); // Enable command this.addCommand({ id: `enable-${id}`, name: `Enable ${name}`, callback: async () => { await this.updateSettings({[setting]: true}); }, }); // Disable command this.addCommand({ id: `disable-${id}`, name: `Disable ${name}`, callback: async () => { await this.updateSettings({[setting]: false}); }, }); } } } // Example: Using commands programmatically const plugin = app.plugins.getPlugin('open-tab-settings'); // Execute via command palette app.commands.executeCommandById('open-tab-settings:toggle-open-in-new-tab'); app.commands.executeCommandById('open-tab-settings:enable-deduplicate-tabs'); app.commands.executeCommandById('open-tab-settings:disable-deduplicate-tabs'); // Or update settings directly await plugin.updateSettings({ openInNewTab: true }); await plugin.updateSettings({ deduplicateTabs: false }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.