channelTabs: Allow Setting Tab Limit & Limit One Tab Per Server (#467)

* [Feat] channelTabs: Allow Setting Tab Limit

* Created a settings option to set a limit on tabs, so you don't overwhelm your computer by having millions of tabs open.

* [Feat] channelTabs: Allow Limiting Servers to One Tab Each

* [Fix] channelTabs: Bypass Server Tab Limit when using Open in New Tab

* [QOL] channelTabs: Switch to makeRange for Some Values

* Remove comment

---------

Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
Z ❤'s Life 2025-10-22 22:08:04 -04:00 • committed by GitHub
parent 2031927d03
commit e6ef8657cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 54 additions and 4 deletions

View file

@ -28,7 +28,7 @@ const contextMenuPatch: NavContextMenuPatchCallback = (children, props: { channe
action={() => createTab({ action={() => createTab({
guildId: channel.guild_id || "@me", // Normalize for DMs/Group Chats guildId: channel.guild_id || "@me", // Normalize for DMs/Group Chats
channelId: channel.id channelId: channel.id
}, settings.store.openInNewTabAutoSwitch, messageId)} }, settings.store.openInNewTabAutoSwitch, messageId, true, true)} // The true values are important for bypassing tab limits
/> />
); );
}; };

View file

@ -8,7 +8,7 @@ import { definePluginSettings } from "@api/Settings";
import { Heading } from "@components/Heading"; import { Heading } from "@components/Heading";
import { Paragraph } from "@components/Paragraph"; import { Paragraph } from "@components/Paragraph";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { OptionType } from "@utils/types"; import { makeRange, OptionType } from "@utils/types";
import { SearchableSelect, useState } from "@webpack/common"; import { SearchableSelect, useState } from "@webpack/common";
import { JSX } from "react"; import { JSX } from "react";
@ -223,7 +223,7 @@ export const settings = definePluginSettings({
hotkeyCount: { hotkeyCount: {
type: OptionType.SLIDER, type: OptionType.SLIDER,
description: "Number of tabs accessible via keyboard shortcuts", description: "Number of tabs accessible via keyboard shortcuts",
markers: [1, 2, 3, 4, 5, 6, 7, 8, 9], markers: makeRange(1, 9, 1),
default: 3, default: 3,
stickToMarkers: true, stickToMarkers: true,
}, },
@ -357,5 +357,19 @@ export const settings = definePluginSettings({
description: "Open all newly created tabs in compact mode by default", description: "Open all newly created tabs in compact mode by default",
default: false, default: false,
restartNeeded: false restartNeeded: false
},
oneTabPerServer: {
type: OptionType.BOOLEAN,
description: "Limit to one tab per server, so opening a new channel in that server will use the existing tab.",
default: false,
restartNeeded: false
},
maxOpenTabs: {
type: OptionType.SLIDER,
description: "Maximum number of open tabs (0 = unlimited)",
markers: makeRange(0, 20, 1),
default: 0,
stickToMarkers: true,
restartNeeded: false
} }
}); });

View file

@ -113,7 +113,43 @@ let clearGhostTabs = () => {
logger.warn("Clear ghost tab function not set"); logger.warn("Clear ghost tab function not set");
}; };
export function createTab(props: BasicChannelTabsProps | ChannelTabsProps, switchToTab?: boolean, messageId?: string, save = true) { export function createTab(props: BasicChannelTabsProps | ChannelTabsProps, switchToTab?: boolean, messageId?: string, save = true, bypassOneTabPerServer = false) {
// Important for the "One tab per server" feature, has to be before the maxOpenTabs check!
if (!bypassOneTabPerServer && settings.store.oneTabPerServer && props.guildId && props.guildId !== "@me") {
const existingTab = openTabs.find(tab => tab.guildId === props.guildId);
if (existingTab) {
existingTab.channelId = props.channelId;
existingTab.messageId = messageId;
existingTab.compact = "compact" in props ? props.compact : settings.store.openNewTabsInCompactMode;
if (switchToTab) {
moveToTab(existingTab.id);
} else {
update(save);
}
return;
}
}
const maxTabs = settings.store.maxOpenTabs;
const isLimitEnabled = maxTabs > 0;
const wouldExceedLimit = isLimitEnabled && openTabs.length >= maxTabs;
if (wouldExceedLimit) {
const currentTab = openTabs.find(t => t.id === currentlyOpenTab);
if (currentTab) {
currentTab.channelId = props.channelId;
currentTab.guildId = props.guildId;
currentTab.messageId = messageId;
currentTab.compact = "compact" in props ? props.compact : settings.store.openNewTabsInCompactMode;
if (switchToTab) {
update(save);
} else {
moveToTab(currentTab.id);
}
}
return;
}
const id = genId(); const id = genId();
openTabs.push({ ...props, id, messageId, compact: "compact" in props ? props.compact : settings.store.openNewTabsInCompactMode }); openTabs.push({ ...props, id, messageId, compact: "compact" in props ? props.compact : settings.store.openNewTabsInCompactMode });
if (switchToTab) moveToTab(id); if (switchToTab) moveToTab(id);