Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/trubbel/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"description": "↓ Lots of features ↓\n\n**Chat:**\n\n– Clickable Steam Inspect Links\n\n– Custom Commands (accountage, followage, localmod/sub etc.)\n\n– BTTV-like Moderation\n\n– Channel Name in Popout Chat\n\n– Clickable Usernames in /mods, /vips and Raid Messages\n\n**Directory:**\n\n– Display Channel Follow Dates\n\n– Display Total Followed Channels\n\n– Live Thumbnail Previews\n\n**Inventory:**\n\n– Auto Claim Drops\n\n– Collapsible Drops\n\n**Livestream:**\n\n– Auto Mute/Pause Stream (in background tabs)\n\n**Mod View:**\n\n– Activity Feed Moderation\n\n– Auto Mod View\n\n**Overall:**\n\n– Live Sidebar Previews\n\n– Yet Another Prime Reminder\n\n**Player:**\n\n– Auto Reset Player\n\n**VODs:**\n\n– Auto Skip Muted Segments\n\n– Custom Seeking\n\n– Custom Progress Bar\n\n…among other things…",
"author": "Trubbel",
"maintainer": "Trubbel",
"version": "4.2.2",
"version": "4.2.3",
"search_terms": "trubbel",
"website": "https://twitch.tv/trubbel",
"settings": "add_ons.trubbel_s_utilities",
"created": "2025-01-06T23:29:54.496Z",
"updated": "2025-10-15T03:35:15.090Z"
"updated": "2025-10-19T17:30:48.508Z"
}
2 changes: 1 addition & 1 deletion src/trubbel/modules/appearance/declutter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default class Declutter {
"hide-sidebar-treasure-train": ".side-nav-card div:has(> .hype-train-icon__train--treasure)",
"hide-sidebar-hype-train": ".side-nav-card div:has(> .hype-train-icon__train--default)",
"hide-sidebar-gift-discount": ".side-nav-card div:has(> [class*=\"giftGradient--\"])",
"hide-player-cc": "[data-a-target=\"player-settings-menu\"] div:has(> button.tw-interactable [d=\"M2 5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5zm2 0h12v10H4V5z\"])",
"hide-player-cc": "[data-a-target=\"player-settings-menu\"] div:has(> button.tw-interactable [d=\"M2 5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5zm2 0h12v10H4V5z\"]),[data-a-target=\"player-settings-menu\"] div:has(> button.tw-interactable [d=\"M4 3a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H4zm1.744 5.962a1.93 1.93 0 0 1 3.598-.395l.105.21-.894.447-.105-.21a.93.93 0 0 0-1.734.19l-.007.028a3.166 3.166 0 0 0 0 1.536l.007.028a.93.93 0 0 0 1.734.19l.105-.21.894.448-.105.21a1.93 1.93 0 0 1-3.598-.396l-.007-.027a4.166 4.166 0 0 1 0-2.021l.007-.028zm5 0a1.93 1.93 0 0 1 3.598-.395l.105.21-.894.447-.105-.21a.93.93 0 0 0-1.734.19l-.007.028a3.165 3.165 0 0 0 0 1.536l.007.028a.93.93 0 0 0 1.734.19l.105-.21.894.448-.105.21a1.93 1.93 0 0 1-3.598-.396l-.007-.027a4.168 4.168 0 0 1 0-2.021l.007-.028z\"])",
"hide-player-disclosure": ".disclosure-tool",
"hide-player-mrv": ".video-player__overlay :is(.player-overlay-background--darkness-3):has(.offline-recommendations-video-card)",
"hide-stories": "#side-nav [class*=\"storiesLeftNavSection--\"],#side-nav :is([style]) :has([class*=\"storiesLeftNavSectionCollapsedButton--\"]),div[class^=\"Layout-sc-\"]:has(> .scrollable-area[style] > div[style] > h2.sr-only)",
Expand Down
11 changes: 11 additions & 0 deletions src/trubbel/modules/appearance/tweaks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ export default class Tweaks {
} else {
this.style.delete("viewer-list-padding");
}
// Appearance - Tweaks - Directory - Full-width in directory
if (this.settings.get("addon.trubbel.appearance.tweaks.directory.max_width")) {
this.style.set("directory-max-width", `
main.twilight-main .common-centered-column,
main.twilight-main .directory-header-new__info {
max-width: unset !important;
}
`);
} else {
this.style.delete("directory-max-width");
}
// Appearance - Tweaks - Inventory - Display bigger images in the inventory page
if (this.settings.get("addon.trubbel.appearance.tweaks.inventory.big_img")) {
this.style.set("inv-big-img", `
Expand Down
195 changes: 195 additions & 0 deletions src/trubbel/modules/channel/chat/message-highlight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { BAD_USERS } from "../../../utilities/constants/types";

const { createElement, on, off } = FrankerFaceZ.utilities.dom;

export default class MessageHighlight {
constructor(parent) {
this.parent = parent;
this.settings = parent.settings;
this.router = parent.router;
this.style = parent.style;
this.site = parent.site;
this.log = parent.log;

this.isActive = false;
this.currentHoveredUser = null;

this.handleNavigation = this.handleNavigation.bind(this);
this.enableMessageHighlight = this.enableMessageHighlight.bind(this);
this.disableMessageHighlight = this.disableMessageHighlight.bind(this);
this.handleSettingChange = this.handleSettingChange.bind(this);
this.handleMessageHover = this.handleMessageHover.bind(this);
this.handleMessageLeave = this.handleMessageLeave.bind(this);
}

initialize() {
const enabled = this.settings.get("addon.trubbel.channel.chat.messages.highlight");
if (enabled > 0) {
this.handleNavigation();
} else {
this.disableMessageHighlight();
}
}

handleSettingChange(enabled) {
if (enabled > 0) {
this.log.info("[Message Highlight] Enabling message highlight");
this.handleNavigation();
} else {
this.log.info("[Message Highlight] Disabling message highlight");
this.disableMessageHighlight();
}
}

handleNavigation() {
const chatRoutes = this.site.constructor.CHAT_ROUTES;
const currentRoute = this.router?.current?.name;

let pathname;

if (this.router?.match && this.router.match[1]) {
pathname = this.router.match[1];
} else {
const location = this.router?.location;
const segment = location?.split("/").filter(segment => segment.length > 0);
pathname = segment?.[0];
}

if (chatRoutes.includes(currentRoute) && pathname && !BAD_USERS.includes(pathname)) {
const enabled = this.settings.get("addon.trubbel.channel.chat.messages.highlight");
if (enabled > 0 && !this.isActive) {
this.log.info("[Message Highlight] Entering chat page, enabling message highlight");
this.enableMessageHighlight();
}
} else {
if (this.isActive) {
this.log.info("[Message Highlight] Leaving chat page, disabling message highlight");
this.disableMessageHighlight();
}
}
}

enableMessageHighlight() {
if (this.isActive) return;

this.log.info("[Message Highlight] Setting up message highlight");
this.isActive = true;

this.addEventListeners();
}

disableMessageHighlight() {
if (!this.isActive) return;

this.log.info("[Message Highlight] Removing message highlight");
this.isActive = false;

if (this.currentHoveredUser) {
this.style.delete("trubbel-message-highlight");
this.currentHoveredUser = null;
}

this.removeEventListeners();
}

addEventListeners() {
const chatContainer = document.querySelector(".chat-scrollable-area__message-container");
if (chatContainer) {
on(chatContainer, "mouseover", this.handleMessageHover);
on(chatContainer, "mouseout", this.handleMessageLeave);
}

this.observer = new MutationObserver(() => {
const container = document.querySelector(".chat-scrollable-area__message-container");
if (container && !container.__trubbel_highlight_attached) {
container.__trubbel_highlight_attached = true;
on(container, "mouseover", this.handleMessageHover);
on(container, "mouseout", this.handleMessageLeave);
}
});

this.observer.observe(document.body, {
childList: true,
subtree: true
});
}

removeEventListeners() {
const containers = document.querySelectorAll(".chat-scrollable-area__message-container");
containers.forEach(container => {
off(container, "mouseover", this.handleMessageHover);
off(container, "mouseout", this.handleMessageLeave);
delete container.__trubbel_highlight_attached;
});

if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
}

handleMessageHover(event) {
if (!this.isActive) return;

const highlightMode = this.settings.get("addon.trubbel.channel.chat.messages.highlight");

let target;
if (highlightMode === 1) {
// Usernames only
target = event.target.closest(".chat-line__username");
} else if (highlightMode === 2) {
// Entire messages
target = event.target.closest(".chat-line__message");
}

if (!target) return;

const messageLine = target.closest(".chat-line__message");
if (!messageLine) return;

const username = messageLine.dataset.user;
if (!username || username === this.currentHoveredUser) return;

this.currentHoveredUser = username;
this.applyHighlight(username);
}

handleMessageLeave(event) {
if (!this.isActive || !this.currentHoveredUser) return;

const highlightMode = this.settings.get("addon.trubbel.channel.chat.messages.highlight");

let target;
if (highlightMode === 1) {
// Usernames only
target = event.target.closest(".chat-line__username");
} else if (highlightMode === 2) {
// Entire messages
target = event.target.closest(".chat-line__message");
}

if (!target) return;

const relatedTarget = event.relatedTarget;
if (relatedTarget) {
if (highlightMode === 1 && relatedTarget.closest(".chat-line__username")) return;
if (highlightMode === 2 && relatedTarget.closest(".chat-line__message")) return;
}

this.currentHoveredUser = null;
this.style.delete("trubbel-message-highlight");
}

applyHighlight(username) {
const color = this.settings.get("addon.trubbel.channel.chat.messages.highlight.color");
if (!color) return;

this.style.set("trubbel-message-highlight", `
body .chat-room .chat-scrollable-area__message-container > div:nth-child(1n+0) > .chat-line__message:not(.chat-line--inline)[data-user="${username}"],
body .chat-room .chat-scrollable-area__message-container > div:nth-child(1n+0) > div > .chat-line__message:not(.chat-line--inline)[data-user="${username}"],
body .chat-room .chat-line__message:not(.chat-line--inline):nth-child(1n+0)[data-user="${username}"] {
background-color: ${color} !important;
}
`);
}
}
15 changes: 15 additions & 0 deletions src/trubbel/settings/appearance/tweaks.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,21 @@ export class Appearance_Tweaks extends FrankerFaceZ.utilities.module.Module {



// Appearance - Tweaks - Directory - Full-width in directory
this.settings.add("addon.trubbel.appearance.tweaks.directory.max_width", {
default: false,
ui: {
sort: 0,
path: "Add-Ons > Trubbel\u2019s Utilities > Appearance > Tweaks >> Directory",
title: "Full-width in directory",
description: "Removes the `max-width` in the directory pages to reduce empty spaces.",
component: "setting-check-box"
},
changed: () => this.tweaks.updateCSS()
});



// Appearance - Tweaks - Inventory - Display bigger images in the inventory page
this.settings.add("addon.trubbel.appearance.tweaks.inventory.big_img", {
default: false,
Expand Down
45 changes: 45 additions & 0 deletions src/trubbel/settings/channel/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Commands from "../../modules/channel/chat/commands-handler";
import FirstTimeChatter from "../../modules/channel/chat/ftc";
import InfoMessage from "../../modules/channel/chat/info-message";
import ChatMarkdown from "../../modules/channel/chat/markdown";
import MessageHighlight from "../../modules/channel/chat/message-highlight";
import OldClipFormat from "../../modules/channel/chat/old-clip-format";
import OldViewerList from "../../modules/channel/chat/old-viewer-list";
import PopoutChatName from "../../modules/channel/chat/popout-header-name";
Expand Down Expand Up @@ -35,6 +36,7 @@ export class Channel_Chat extends FrankerFaceZ.utilities.module.Module {
this.firstTimeChatter = new FirstTimeChatter(this);
this.infoMessage = new InfoMessage(this);
this.chatMarkdown = new ChatMarkdown(this);
this.messageHighlight = new MessageHighlight(this);
this.oldClipFormat = new OldClipFormat(this);
this.oldViewerList = new OldViewerList(this);
this.popoutChatName = new PopoutChatName(this);
Expand Down Expand Up @@ -164,6 +166,47 @@ export class Channel_Chat extends FrankerFaceZ.utilities.module.Module {



// Channel - Chat - Messages - Highlight Messages on Hover
this.settings.add("addon.trubbel.channel.chat.messages.highlight", {
default: 0,
ui: {
path: "Add-Ons > Trubbel\u2019s Utilities > Channel > Chat >> Messages",
title: "Highlight Messages on Hover",
description: "Highlight all messages from a user.",
component: "setting-select-box",
data: [
{ value: 0, title: "Off" },
{ value: 1, title: "Username" },
{ value: 2, title: "Entire Message" }
]
},
changed: val => this.messageHighlight.handleSettingChange(val)
});

// Channel - Chat - Messages - Hover Highlight
this.settings.add("addon.trubbel.channel.chat.messages.highlight.color", {
default: "rgba(169, 112, 255, 0.5)",
requires: ["addon.trubbel.channel.chat.messages.highlight"],
process(ctx, val) {
if (!ctx.get("addon.trubbel.channel.chat.messages.highlight"))
return false;
return val;
},
ui: {
path: "Add-Ons > Trubbel\u2019s Utilities > Channel > Chat >> Messages",
title: "Hover Highlight",
description: "Background color for highlighted messages.",
component: "setting-color-box"
},
changed: () => {
if (this.messageHighlight?.currentHoveredUser) {
this.messageHighlight.applyHighlight(this.messageHighlight.currentHoveredUser);
}
}
});



// Channel - Chat - Moderation - Enable right-click context menu
this.settings.add("addon.trubbel.channel.chat.moderation.context", {
default: false,
Expand Down Expand Up @@ -351,6 +394,7 @@ export class Channel_Chat extends FrankerFaceZ.utilities.module.Module {
this.firstTimeChatter.initialize();
this.infoMessage.initialize();
this.chatMarkdown.initialize();
this.messageHighlight.initialize();
this.oldClipFormat.initialize();
this.oldViewerList.initialize();
this.popoutChatName.initialize();
Expand All @@ -368,6 +412,7 @@ export class Channel_Chat extends FrankerFaceZ.utilities.module.Module {
this.firstTimeChatter.handleNavigation();
this.infoMessage.handleNavigation();
this.chatMarkdown.handleNavigation();
this.messageHighlight.handleNavigation();
this.oldClipFormat.handleNavigation();
this.oldViewerList.handleNavigation();
this.popoutChatName.handleNavigation();
Expand Down