Greasy Fork 支持简体中文。

更佳 YouTube 劇場模式

改善 YouTube 劇場模式,參考 Twitch.tv 的設計,增強影片與聊天室佈局,同時維持效能與相容性。並修復近期 YouTube 更新中損壞的全螢幕介面。

安裝腳本?
作者推薦腳本

您可能也會喜歡 Youtube HD Premium

安裝腳本
// ==UserScript==
// @name                Better Theater Mode for YouTube
// @name:zh-TW          更佳 YouTube 劇場模式
// @name:zh-CN          更佳 YouTube 剧场模式
// @name:ja             より良いYouTubeシアターモード
// @icon                https://www.youtube.com/img/favicon_48.png
// @author              ElectroKnight22
// @namespace           electroknight22_youtube_better_theater_mode_namespace
// @version             1.8.1
// @match               *://www.youtube.com/*
// @match               *://www.youtube-nocookie.com/*
// @grant               GM.getValue
// @grant               GM.setValue
// @grant               GM.deleteValue
// @grant               GM.listValues
// @grant               GM.registerMenuCommand
// @grant               GM.unregisterMenuCommand
// @grant               GM.notification
// @license             MIT
// @description         Improves YouTube's theater mode with a Twitch.tv-like design, enhancing video and chat layouts, while maintaining performance and compatibility. Also fixes the broken fullscreen UI from the recent YouTube update.
// @description:zh-TW   改善 YouTube 劇場模式,參考 Twitch.tv 的設計,增強影片與聊天室佈局,同時維持效能與相容性。並修復近期 YouTube 更新中損壞的全螢幕介面。
// @description:zh-CN   改进 YouTube 剧场模式,参考 Twitch.tv 的设计,增强视频与聊天室布局,同时保持性能与兼容性。并修复近期 YouTube 更新中损坏的全屏界面。
// @description:ja      YouTubeのシアターモードを改善し、Twitch.tvのデザインを参考にして、動画とチャットのレイアウトを強化しつつ、パフォーマンスと互換性を維持します。また、最近のYouTubeアップデートによる壊れたフルスクリーンUIを修正します。
// ==/UserScript==

/*jshint esversion: 11 */

(function () {
    "use strict";

    // -------------------------------
    // Default settings for storage under the "settings" key.
    // Blacklist is now stored separately.
    // -------------------------------
    const DEFAULT_SETTINGS = {
        isScriptActive: true,
        isSimpleMode: true,
        enableOnlyForLiveStreams: false,
        modifyVideoPlayer: true,
        modifyChat: true,
        setLowHeadmast: false
    };
    const DEFAULT_BLACKLIST = []; // default blacklist (empty)

    // -------------------------------
    // Other constants and translations
    // -------------------------------
    const BROWSER_LANGUAGE = navigator.language || navigator.userLanguage;
    const GET_PREFERRED_LANGUAGE = () => {
        if (BROWSER_LANGUAGE.startsWith('zh') && BROWSER_LANGUAGE !== 'zh-TW') {
            return 'zh-CN';
        } else {
            return BROWSER_LANGUAGE;
        }
    };

    const TRANSLATIONS = {
        'en-US': {
            tampermonkeyOutdatedAlertMessage: "It looks like you're using an older version of Tampermonkey that might cause menu issues. For the best experience, please update to version 5.4.6224 or later.",
            turnOn: 'Turn On',
            turnOff: 'Turn Off',
            livestreamOnlyMode: 'Livestream Only Mode',
            applyChatStyles: 'Apply Chat Styles',
            applyVideoPlayerStyles: 'Apply Video Player Styles',
            moveHeadmastBelowVideoPlayer: 'Move Headmast Below Video Player',
            blacklistVideo: 'Blacklist Video',
            unblacklistVideo: 'Unblacklist Video',
            simpleMode: 'Simple Mode',
            advancedMode: 'Advanced Mode',
            debug: 'DEBUG'
        },
        'zh-TW': {
            tampermonkeyOutdatedAlertMessage: "看起來您正在使用較舊版本的篡改猴,可能會導致選單問題。為了獲得最佳體驗,請更新至 5.4.6224 或更高版本。",
            turnOn: '開啟',
            turnOff: '關閉',
            livestreamOnlyMode: '僅限直播模式',
            applyChatStyles: '套用聊天樣式',
            applyVideoPlayerStyles: '套用影片播放器樣式',
            moveHeadmastBelowVideoPlayer: '將頁首橫幅移到影片播放器下方',
            blacklistVideo: '將影片加入黑名單',
            unblacklistVideo: '從黑名單中移除影片',
            simpleMode: '簡易模式',
            advancedMode: '進階模式',
            debug: '偵錯'
        },
        'zh-CN': {
            tampermonkeyOutdatedAlertMessage: "看起来您正在使用旧版本的篡改猴,这可能会导致菜单问题。为了获得最佳体验,请更新到 5.4.6224 或更高版本。",
            turnOn: '开启',
            turnOff: '关闭',
            livestreamOnlyMode: '仅限直播模式',
            applyChatStyles: '应用聊天样式',
            applyVideoPlayerStyles: '应用视频播放器样式',
            moveHeadmastBelowVideoPlayer: '将页首横幅移动到视频播放器下方',
            blacklistVideo: '将视频加入黑名单',
            unblacklistVideo: '从黑名单中移除视频',
            simpleMode: '简易模式',
            advancedMode: '高级模式',
            debug: '调试'
        },
        'ja': {
            tampermonkeyOutdatedAlertMessage: "ご利用のTampermonkeyのバージョンが古いため、メニューに問題が発生する可能性があります。より良い体験のため、バージョン5.4.6224以上に更新してください。",
            turnOn: "オンにする",
            turnOff: "オフにする",
            livestreamOnlyMode: "ライブ配信専用モード",
            applyChatStyles: "チャットスタイルを適用",
            applyVideoPlayerStyles: "ビデオプレイヤースタイルを適用",
            moveHeadmastBelowVideoPlayer: "ヘッドマストをビデオプレイヤーの下に移動",
            blacklistVideo: "動画をブラックリストに追加",
            unblacklistVideo: "ブラックリストから動画を解除",
            simpleMode: "シンプルモード",
            advancedMode: "高度モード",
            debug: "デバッグ"
        }
    };

    const GET_LOCALIZED_TEXT = () => {
        const language = GET_PREFERRED_LANGUAGE();
        return TRANSLATIONS[language] || TRANSLATIONS['en-US'];
    };

    // -------------------------------
    // Global variables for dynamic state
    // -------------------------------
    let userSettings = { ...DEFAULT_SETTINGS };
    let blacklist = new Set();
    let userSettingsBackup = { ...DEFAULT_SETTINGS };
    let useCompatibilityMode = false;
    let menuItems = new Set();
    let activeStyles = new Map();
    let resizeObserver;
    let moviePlayer;
    let videoId;
    let chatFrame;
    let currentPageType = '';
    let isFullscreen = false;
    let isTheaterMode = false;
    let chatCollapsed = true;
    let isLiveStream = false;
    let chatWidth = 0;
    let moviePlayerHeight = 0;
    let isOldTampermonkey = false;
    const updatedVersions = {
        Tampermonkey: '5.4.624'
    };
    let isScriptRecentlyUpdated = false;

    // -------------------------------
    // Greasemonkey API Compatibility Layer
    // -------------------------------
    // (Note: We no longer use GM for adding styles; style insertion is done by our custom function.)
    const GMCustomRegisterMenuCommand = useCompatibilityMode ? GM_registerMenuCommand : GM.registerMenuCommand;
    const GMCustomUnregisterMenuCommand = useCompatibilityMode ? GM_unregisterMenuCommand : GM.unregisterMenuCommand;
    const GMCustomGetValue = useCompatibilityMode ? GM_getValue : GM.getValue;
    const GMCustomSetValue = useCompatibilityMode ? GM_setValue : GM.setValue;
    const GMCustomListValues = useCompatibilityMode ? GM_listValues : GM.listValues;
    const GMCustomDeleteValue = useCompatibilityMode ? GM_deleteValue : GM.deleteValue;
    const GMCustomNotification = useCompatibilityMode ? GM_notification : GM.notification;

    // -------------------------------
    // (Existing) Style Rules & Functions
    // -------------------------------
    const styleRules = {
        chatStyle: {
            id: "chatStyle",
            getRule: () => `
                ytd-live-chat-frame[theater-watch-while][rounded-container] {
                    border-radius: 0 !important;
                    border-top: 0 !important;
                }
                ytd-watch-flexy[fixed-panels] #chat.ytd-watch-flexy {
                    top: 0 !important;
                    border-top: 0 !important;
                    border-bottom: 0 !important;
                }
            `,
        },
        videoPlayerStyle: {
            id: "videoPlayerStyle",
            getRule: () => `
                ytd-watch-flexy[full-bleed-player] #full-bleed-container.ytd-watch-flexy {
                    max-height: calc(100vh - var(--ytd-watch-flexy-masthead-height)) !important;
                }
            `,
        },
        headmastStyle: {
            id: "headmastStyle",
            getRule: () => `
                #masthead-container.ytd-app {
                    max-width: calc(100% - ${chatWidth}px) !important;
                }
            `,
        },
        lowHeadmastStyle: {
            id: "lowHeadmastStyle",
            getRule: () => `
                #page-manager.ytd-app {
                    margin-top: 0 !important;
                    top: calc(-1 * var(--ytd-toolbar-offset)) !important;
                    position: relative !important;
                }
                ytd-watch-flexy[flexy]:not([full-bleed-player][full-bleed-no-max-width-columns]) #columns.ytd-watch-flexy {
                    margin-top: var(--ytd-toolbar-offset) !important;
                }
                ${userSettings.modifyVideoPlayer ? `
                    ytd-watch-flexy[full-bleed-player] #full-bleed-container.ytd-watch-flexy {
                        max-height: 100vh !important;
                    }
                ` : ''}
                #masthead-container.ytd-app {
                    z-index: 599 !important;
                    top: ${moviePlayerHeight}px !important;
                    position: relative !important;
                }
            `,
        },
        videoPlayerFixStyle: {
            id: "staticVideoPlayerFixStyle",
            getRule: () => `
                .html5-video-container {
                    top: -1px !important;
                }
                #skip-navigation.ytd-masthead {
                    left: -500px;
                }
            `,
        },
        chatFrameFixStyle: {
            id: "staticChatFrameFixStyle",
            getRule: () => {
                const chatInputContainer = document.querySelector("tp-yt-iron-pages#panel-pages.style-scope.yt-live-chat-renderer");
                const shouldHideChatInputContainerTopBorder = chatInputContainer?.clientHeight === 0;
                const borderTopStyle = shouldHideChatInputContainerTopBorder ? 'border-top: 0 !important;' : '';
                return `
                    #panel-pages.yt-live-chat-renderer {
                        ${borderTopStyle}
                        border-bottom: 0 !important;
                    }
                `;
            },
        },
        chatRendererFixStyle: {
            id: "staticChatRendererFixStyle",
            getRule: () => `
                ytd-live-chat-frame[theater-watch-while][rounded-container] {
                    border-bottom: 0 !important;
                }
            `,
        },
    };

    function removeStyle(style) {
        if (!activeStyles.has(style.id)) return;
        const { element: styleElement } = activeStyles.get(style.id);
        if (styleElement && styleElement.parentNode) {
            styleElement.parentNode.removeChild(styleElement);
        }
        activeStyles.delete(style.id);
    }

    function removeAllStyles() {
        activeStyles.forEach((styleData, styleId) => {
            if (!styleData.persistent) {
                removeStyle({ id: styleId });
            }
        });
    }

    // Use our custom style insertion helper.
    function applyStyle(style, setPersistent = false) {

        if (typeof style.getRule !== 'function') return;
        if (activeStyles.has(style.id)) removeStyle(style);
        const styleElement = addStyleHelper(style.getRule());
        activeStyles.set(style.id, { element: styleElement, persistent: setPersistent });

        function addStyleHelper(css) {
            const head = document.head || document.documentElement;
            const styleElem = document.createElement('style');
            styleElem.type = 'text/css';
            styleElem.textContent = css;
            head.appendChild(styleElem);
            return styleElem;
        }
    }

    function setStyleState(style, on = true) {
        on ? applyStyle(style) : removeStyle(style);
    }

    function updateLowHeadmastStyle() {
        if (!moviePlayer) return;
        const shouldApplyLowHeadmast = userSettings.setLowHeadmast && isTheaterMode && !isFullscreen && currentPageType === 'watch';
        setStyleState(styleRules.lowHeadmastStyle, shouldApplyLowHeadmast);
    }

    function updateHeadmastStyle() {
        updateLowHeadmastStyle();
        let shouldShrinkHeadmast = isTheaterMode &&
            chatFrame?.getAttribute('theater-watch-while') === '' &&
            (userSettings.setLowHeadmast || userSettings.modifyChat);
        chatWidth = chatFrame?.offsetWidth || 0;
        setStyleState(styleRules.headmastStyle, shouldShrinkHeadmast);
    }

    function updateStyles() {
        try {
            const shouldNotActivate =
                !userSettings.isScriptActive ||
                (blacklist && blacklist.has(videoId)) ||
                (userSettings.enableOnlyForLiveStreams && !isLiveStream);
            if (shouldNotActivate) {
                removeAllStyles();
                if (moviePlayer) moviePlayer.setCenterCrop(); // trigger update for html5 video element
                return;
            }
            setStyleState(styleRules.chatStyle, userSettings.modifyChat);
            setStyleState(styleRules.videoPlayerStyle, userSettings.modifyVideoPlayer);
            updateHeadmastStyle();
            if (moviePlayer) moviePlayer.setCenterCrop();
        } catch (error) {
            console.log(`Error when trying to update styles: ${error}.`);
        }
    }

    function updateFullscreenStatus() {
        isFullscreen = !!document.fullscreenElement;
    }

    function updateTheaterStatus(event) {
        isTheaterMode = !!event?.detail?.enabled;
        updateStyles();
    }

    function updateChatStatus(event) {
        chatFrame = event.target;
        chatCollapsed = event.detail !== false;
        window.addEventListener('player-api-ready', () => { updateStyles(); }, { once: true });
    }

    function updateMoviePlayer() {
        const newMoviePlayer = document.querySelector('#movie_player');
        if (!resizeObserver) {
            resizeObserver = new ResizeObserver(entries => {
                moviePlayerHeight = moviePlayer.offsetHeight;
                updateStyles();
            });
        }
        if (moviePlayer) resizeObserver.unobserve(moviePlayer);
        moviePlayer = newMoviePlayer;
        if (moviePlayer) resizeObserver.observe(moviePlayer);
    }

    function updateVideoStatus(event) {
        try {
            currentPageType = event.detail.pageData.page;
            videoId = event.detail.pageData.playerResponse.videoDetails.videoId;
            updateMoviePlayer();
            isLiveStream = event.detail.pageData.playerResponse.videoDetails.isLiveContent;
            showMenuOptions();
        } catch (error) {
            throw ("Failed to update video status due to this error. Error: " + error);
        }
    }

    // -------------------------------
    // New Storage Helper Functions
    // -------------------------------
    async function loadUserSettings() {
        try {
            userSettings = await GMCustomGetValue('settings', DEFAULT_SETTINGS);
            console.log(`Loaded user settings: ${JSON.stringify(userSettings)}`);
        } catch (error) {
            throw `Error loading user settings: ${error}. Aborting script.`;
        }
    }

    async function updateSetting(key, value) {
        try {
            let currentSettings = await GMCustomGetValue('settings', DEFAULT_SETTINGS);
            currentSettings[key] = value;
            await GMCustomSetValue('settings', currentSettings);
        } catch (error) {
            console.log("Error updating setting: " + error);
        }
    }

    async function loadBlacklist() {
        try {
            let storedBlacklist = await GMCustomGetValue('blacklist', DEFAULT_BLACKLIST);
            if (Array.isArray(storedBlacklist)) {
                blacklist = new Set(storedBlacklist);
            } else {
                blacklist = new Set();
            }
            console.log(`Loaded blacklist: ${JSON.stringify(Array.from(blacklist))}`);
        } catch (error) {
            throw `Error loading blacklist: ${error}. Aborting script.`;
        }
    }

    async function updateBlacklist() {
        try {
            await GMCustomSetValue('blacklist', Array.from(blacklist));
        } catch (error) {
            console.log("Error updating blacklist: " + error);
        }
    }

    async function updateScriptInfo() {
        try {
            const oldScriptInfo = await GMCustomGetValue('scriptInfo', null);
            console.log(`Previous script info: ${JSON.stringify(oldScriptInfo)}`);
            const newScriptInfo = {
                version: getScriptVersionFromMeta(),
            };
            await GMCustomSetValue('scriptInfo', newScriptInfo);
            console.log(`Updated script info: ${JSON.stringify(newScriptInfo)}`);
            if (!oldScriptInfo || compareVersions(newScriptInfo.version, oldScriptInfo?.version) !== 0) {
                isScriptRecentlyUpdated = true;
            }
        } catch (error) {
            console.log("Error updating script info: " + error);
        }
    }

    async function cleanupOldStorage() {
        try {
            const allowedKeys = ['settings', 'scriptInfo', 'blacklist'];
            const keys = await GMCustomListValues();
            for (const key of keys) {
                if (!allowedKeys.includes(key)) {
                    await GMCustomDeleteValue(key);
                    console.log(`Deleted leftover key: ${key}`);
                }
            }
        } catch (error) {
            console.log("Error cleaning up old storage keys: " + error);
        }
    }

    function getScriptVersionFromMeta() {
        const meta = GM_info.scriptMetaStr;
        const versionMatch = meta.match(/@version\s+([^\r\n]+)/);
        return versionMatch ? versionMatch[1].trim() : null;
    }

    // -------------------------------
    // Updated Menu Management using new storage functions
    // -------------------------------
    function processMenuOptions(options, callback) {
        Object.values(options).forEach(option => {
            if (!option.alwaysShow && !userSettings.expandMenu) return;
            if (option.items) {
                option.items.forEach(item => callback(item));
            } else {
                callback(option);
            }
        });
    }

    function removeMenuOptions() {
        menuItems.forEach((menuItem) => {
            GMCustomUnregisterMenuCommand(menuItem);
        });
        menuItems.clear();
    }

    async function showMenuOptions() {
        const shouldAutoClose = isOldTampermonkey;
        removeMenuOptions();
        const advancedMenuOptions = userSettings.isSimpleMode ? {} : {
            toggleOnlyLiveStreamMode: {
                alwaysShow: true,
                label: () => `${userSettings.enableOnlyForLiveStreams ? "✅" : "❌"} ` + GET_LOCALIZED_TEXT().livestreamOnlyMode,
                menuId: "toggleOnlyLiveStreamMode",
                handleClick: async function () {
                    userSettings.enableOnlyForLiveStreams = !userSettings.enableOnlyForLiveStreams;
                    await updateSetting('enableOnlyForLiveStreams', userSettings.enableOnlyForLiveStreams);
                    updateStyles();
                    showMenuOptions();
                },
            },
            toggleChatStyle: {
                alwaysShow: true,
                label: () => `${userSettings.modifyChat ? "✅" : "❌"} ` + GET_LOCALIZED_TEXT().applyChatStyles,
                menuId: "toggleChatStyle",
                handleClick: async function () {
                    userSettings.modifyChat = !userSettings.modifyChat;
                    await updateSetting('modifyChat', userSettings.modifyChat);
                    updateStyles();
                    showMenuOptions();
                },
            },
            toggleVideoPlayerStyle: {
                alwaysShow: true,
                label: () => `${userSettings.modifyVideoPlayer ? "✅" : "❌"} ` + GET_LOCALIZED_TEXT().applyVideoPlayerStyles,
                menuId: "toggleVideoPlayerStyle",
                handleClick: async function () {
                    userSettings.modifyVideoPlayer = !userSettings.modifyVideoPlayer;
                    await updateSetting('modifyVideoPlayer', userSettings.modifyVideoPlayer);
                    updateStyles();
                    showMenuOptions();
                },
            },
            toggleLowHeadmast: {
                alwaysShow: true,
                label: () => `${userSettings.setLowHeadmast ? "✅" : "❌"} ` + GET_LOCALIZED_TEXT().moveHeadmastBelowVideoPlayer,
                menuId: "toggleLowHeadmast",
                handleClick: async function () {
                    userSettings.setLowHeadmast = !userSettings.setLowHeadmast;
                    await updateSetting('setLowHeadmast', userSettings.setLowHeadmast);
                    updateStyles();
                    showMenuOptions();
                },
            },
        };

        const menuOptions = {
            toggleScript: {
                alwaysShow: true,
                label: () => `🔄 ${userSettings.isScriptActive ? GET_LOCALIZED_TEXT().turnOff : GET_LOCALIZED_TEXT().turnOn}`,
                menuId: "toggleScript",
                handleClick: async function () {
                    userSettings.isScriptActive = !userSettings.isScriptActive;
                    await updateSetting('isScriptActive', userSettings.isScriptActive);
                    updateStyles();
                    showMenuOptions();
                },
            },
            ...advancedMenuOptions,
            addVideoToBlacklist: {
                alwaysShow: true,
                label: () => `🚫 ${blacklist.has(videoId) ? GET_LOCALIZED_TEXT().unblacklistVideo : GET_LOCALIZED_TEXT().blacklistVideo} [id: ${videoId}]`,
                menuId: "addVideoToBlacklist",
                handleClick: async function () {
                    if (blacklist.has(videoId)) {
                        blacklist.delete(videoId);
                    } else {
                        blacklist.add(videoId);
                    }
                    await updateBlacklist();
                    updateStyles();
                    showMenuOptions();
                },
            },
            toggleSimpleMode: {
                alwaysShow: true,
                label: () => `${userSettings.isSimpleMode ? "🚀 " + GET_LOCALIZED_TEXT().simpleMode : "🔧 " + GET_LOCALIZED_TEXT().advancedMode}`,
                menuId: "toggleSimpleMode",
                handleClick: async function () {
                    const isNewModeSimple = !userSettings.isSimpleMode;
                    if (isNewModeSimple) userSettingsBackup = { ...userSettings };
                    await updateSetting('isSimpleMode', isNewModeSimple);
                    userSettings = isNewModeSimple ? { ...DEFAULT_SETTINGS } : userSettingsBackup;
                    userSettings.isSimpleMode = isNewModeSimple;
                    updateStyles();
                    showMenuOptions();
                },
            },
        };

        processMenuOptions(menuOptions, (item) => {
            GMCustomRegisterMenuCommand(item.label(), item.handleClick, {
                id: item.menuId,
                autoClose: shouldAutoClose,
            });
            menuItems.add(item.menuId);
        });
    }

    function compareVersions(v1, v2) {
        const parts1 = v1.split('.').map(Number);
        const parts2 = v2.split('.').map(Number);
        const len = Math.max(parts1.length, parts2.length);
        for (let i = 0; i < len; i++) {
            const num1 = parts1[i] || 0;
            const num2 = parts2[i] || 0;
            if (num1 > num2) return 1;
            if (num1 < num2) return -1;
        }
        return 0;
    }

    function hasGreasyMonkeyAPI() {
        if (typeof GM !== 'undefined') return true;
        if (typeof GM_info !== 'undefined') {
            useCompatibilityMode = true;
            console.warn("Running in compatibility mode.");
            return true;
        }
        return false;
    }

    function CheckTampermonkeyUpdated() {
        if (GM_info.scriptHandler === "Tampermonkey" &&
            compareVersions(GM_info.version, updatedVersions.Tampermonkey) !== 1) {
            isOldTampermonkey = true;
            if (isScriptRecentlyUpdated) {
                GMCustomNotification({
                    text: GET_LOCALIZED_TEXT().tampermonkeyOutdatedAlertMessage,
                    timeout: 15000
                });
            }
        }
    }

    function attachEventListeners() {
        window.addEventListener('yt-set-theater-mode-enabled', (event) => { updateTheaterStatus(event); }, true);
        window.addEventListener('yt-chat-collapsed-changed', (event) => { updateChatStatus(event); }, true);
        window.addEventListener('yt-page-data-fetched', (event) => { updateVideoStatus(event); }, true);
        window.addEventListener('yt-page-data-updated', updateStyles, true);
        window.addEventListener('fullscreenchange', updateFullscreenStatus, true);
    }

    function isLiveChatIFrame() {
        const liveChatIFramePattern = /^https?:\/\/.*youtube\.com\/live_chat.*$/;
        return liveChatIFramePattern.test(window.location.href);
    }

    // -------------------------------
    // Initialize the script
    // -------------------------------
    async function initialize() {
        try {
            if (!hasGreasyMonkeyAPI()) throw "Did not detect valid Grease Monkey API";
            await cleanupOldStorage();
            await loadUserSettings();
            await loadBlacklist();
            await updateScriptInfo();
            CheckTampermonkeyUpdated();
            if (isLiveChatIFrame()) {
                applyStyle(styleRules.chatFrameFixStyle, true);
                return;
            }
            applyStyle(styleRules.chatRendererFixStyle, true);
            applyStyle(styleRules.videoPlayerFixStyle, true);
            updateStyles();
            attachEventListeners();
            showMenuOptions();
        } catch (error) {
            console.error(`Error when initializing script: ${error}. Aborting script.`);
        }
    }

    // -------------------------------
    // Entry Point
    // -------------------------------
    initialize();
})();