Greasy Fork

LingQ Addon

Provides custom LingQ layouts

当前为 2025-05-10 提交的版本,查看 最新版本

// ==UserScript==
// @name         LingQ Addon
// @description  Provides custom LingQ layouts
// @match        https://www.lingq.com/*/learn/*/web/reader/*
// @match        https://www.lingq.com/*/learn/*/web/library/course/*
// @exclude      https://www.lingq.com/*/learn/*/web/editor/*
// @version      4.2.5
// @grant       GM_setValue
// @grant       GM_getValue
// @namespace https://greasyfork.org/users/1458847
// ==/UserScript==

(function () {
    "use strict";

    // Utility functions
    const storage = {
        get: (key, defaultValue) => {
            const value = GM_getValue(key);
            return value === undefined ? defaultValue : value;
        },
        set: (key, value) => GM_setValue(key, value)
    };

    // Default values
    const defaults = {
        styleType: "video",
        colorMode: "dark",
        fontSize: 1.1,
        lineHeight: 1.7,
        heightBig: 400,
        sentenceHeight: 400,
        darkColors: {
            fontColor: "#e0e0e0",
            lingqBackground: "rgba(109, 89, 44, 0.7)",
            lingqBorder: "rgba(254, 203, 72, 0.3)",
            lingqBorderLearned: "rgba(254, 203, 72, 0.5)",
            blueBorder: "rgba(72, 154, 254, 0.5)",
            playingUnderline: "#ffffff"
        },
        whiteColors: {
            fontColor: "#000000",
            lingqBackground: "rgba(255, 200, 0, 0.4)",
            lingqBorder: "rgba(255, 200, 0, 0.3)",
            lingqBorderLearned: "rgba(255, 200, 0, 1)",
            blueBorder: "rgba(0, 111, 255, 0.3)",
            playingUnderline: "#000000"
        },
        librarySortOption: 0,
        autoFinishing: false
    };

    // Load stored settings
    const settings = {
        styleType: storage.get("styleType", defaults.styleType),
        colorMode: storage.get("colorMode", defaults.colorMode),
        fontSize: storage.get("fontSize", defaults.fontSize),
        lineHeight: storage.get("lineHeight", defaults.lineHeight),
        heightBig: storage.get("heightBig", defaults.heightBig),
        sentenceHeight: storage.get("sentenceHeight", defaults.sentenceHeight),
        librarySortOption: storage.get("librarySortOption", defaults.librarySortOption),
        autoFinishing: storage.get("autoFinishing", defaults.autoFinishing)
    };

    // Helper function to get color settings based on mode
    function getColorSettings(colorMode) {
        const prefix = colorMode === "dark" ? "dark_" : "white_";
        const defaultColors = colorMode === "dark" ? defaults.darkColors : defaults.whiteColors;

        return {
            fontColor: storage.get(prefix + "fontColor", defaultColors.fontColor),
            lingqBackground: storage.get(prefix + "lingqBackground", defaultColors.lingqBackground),
            lingqBorder: storage.get(prefix + "lingqBorder", defaultColors.lingqBorder),
            lingqBorderLearned: storage.get(prefix + "lingqBorderLearned", defaultColors.lingqBorderLearned),
            blueBorder: storage.get(prefix + "blueBorder", defaultColors.blueBorder),
            playingUnderline: storage.get(prefix + "playingUnderline", defaultColors.playingUnderline)
        };
    }

    // Get current color settings
    const colorSettings = getColorSettings(settings.colorMode);

    // UI Creation
    function createUI() {
        // Create settings button
        const settingsButton = createElement("button", {
            id: "lingqAddonSettings",
            textContent: "⚙️",
            title: "LingQ Addon Settings",
            className: "nav-button"
        });

        // Create lesson complete button
        const completeLessonButton = createElement("button", {
            id: "lingqLessonComplete",
            textContent: "✔",
            title: "Complete Lesson Button",
            className: "nav-button"
        });

        // Create download words button
        const downloadWordsButton = createElement("button", {
            id: "lingqDownloadWords",
            textContent: "💾",
            title: "Download Words",
            className: "nav-button"
        });

        // Find the #main-nav element
        let mainNav = document.querySelector(
            "#main-nav > nav > div:nth-child(2) > div:nth-child(1)",
        ) || document.querySelector("#main-nav");

        if (mainNav) {
            mainNav.appendChild(settingsButton);
            mainNav.appendChild(downloadWordsButton);
            mainNav.appendChild(completeLessonButton);
        } else {
            console.error("#main-nav element not found. Buttons not inserted.");
        }

        // Create settings popup
        const settingsPopup = createSettingsPopup();
        document.body.appendChild(settingsPopup);

        // Create download words popup
        const downloadWordsPopup = createDownloadWordsPopup();
        document.body.appendChild(downloadWordsPopup);

        // Add event listeners
        setupSettingEventListeners(settingsButton, settingsPopup);
        setupDownloadWordsEventListeners(downloadWordsButton, downloadWordsPopup);
        setupEventListeners()
    }

    // Create settings popup with all controls
    function createSettingsPopup() {
        const popup = createElement("div", {id: "lingqAddonSettingsPopup"});

        // drag handle
        const dragHandle = createElement("div", {id: "lingqAddonSettingsDragHandle"});

        const dragHandleTitle = createElement("h3", {textContent: "LingQ Addon Settings"});
        dragHandle.appendChild(dragHandleTitle);

        // popup content
        const content = createElement("div", {style: `padding: 0 10px;`});
        const popupContentElement = generatePopupContent();
        content.appendChild(popupContentElement);

        popup.appendChild(dragHandle);
        popup.appendChild(content);

        return popup;
    }

    // Generate HTML content for the popup
    function generatePopupContent() {
        const container = createElement("div");

        addSelect(container, "styleTypeSelector", "Layout Style:", [
            { value: "video", text: "Video" },
            { value: "video2", text: "Video2" },
            { value: "audio", text: "Audio" },
            { value: "off", text: "Off" }
        ], settings.styleType);

        const videoSettings = createElement("div", {
            id: "videoSettings",
            style: `${settings.styleType === "video" ? "" : "display: none"}`
        });
        addSlider(videoSettings, "heightBigSlider", "Video Height:", "heightBigValue", settings.heightBig, "px", 300, 800, 10);
        container.appendChild(videoSettings);

        const sentenceVideoSettings = createElement("div", {
            id: "sentenceVideoSettings",
            style: `${settings.styleType === "off" ? "" : "display: none"}`
        });
        addSlider(sentenceVideoSettings, "sentenceHeightSlider", "Sentence Video Height:", "sentenceHeightValue", settings.heightBig, "px", 300, 600, 10);
        container.appendChild(sentenceVideoSettings);

        addSlider(container, "fontSizeSlider", "Font Size:", "fontSizeValue", settings.fontSize, "rem", 0.8, 1.8, 0.05);
        addSlider(container, "lineHeightSlider", "Line Height:", "lineHeightValue", settings.lineHeight, "", 1.2, 3.0, 0.1);

        const colorSection = createElement("div", {
            style: "border: 1px solid var(--font_color, #e0e0e0); padding: 0 10px; border-radius: 5px;"
        });

        addSelect(colorSection, "colorModeSelector", "Color Mode:", [
            { value: "dark", text: "Dark" },
            { value: "white", text: "White" }
        ], settings.colorMode);

        [
            { id: "fontColor", label: "Font Color:", value: colorSettings.fontColor },
            { id: "lingqBackground", label: "LingQ Background:", value: colorSettings.lingqBackground },
            { id: "lingqBorder", label: "LingQ Border:", value: colorSettings.lingqBorder },
            { id: "lingqBorderLearned", label: "LingQ Border Learned:", value: colorSettings.lingqBorderLearned },
            { id: "blueBorder", label: "Blue Border:", value: colorSettings.blueBorder },
            { id: "playingUnderline", label: "Playing Underline:", value: colorSettings.playingUnderline }
        ].forEach(config => addColorPicker(colorSection, config.id, config.label, config.value));

        container.appendChild(colorSection);

        addCheckbox(container, "autoFinishingCheckbox", "Finish Lesson Automatically", settings.autoFinishing);

        const buttonContainer = createElement("div", {style: "display: flex; justify-content: space-between;", className: "popup-row"});
        [
            {id: "resetSettingsBtn", textContent: "Reset", className: "popup-button"},
            {id: "closeSettingsBtn", textContent: "Close", className: "popup-button"}
        ].forEach((prop) => {
            buttonContainer.appendChild(createElement("button", prop));
        });

        container.appendChild(buttonContainer);
        return container;
    }

    // Create download words popup
    function createDownloadWordsPopup() {
        const popup = createElement("div", {id: "lingqDownloadWordsPopup"});

        // drag handle
        const dragHandle = createElement("div", {id: "lingqDownloadWordsDragHandle"});

        const dragHandleTitle = createElement("h3", {textContent: "Download Words"});
        dragHandle.appendChild(dragHandleTitle);

        const content = createElement("div", {style: `padding: 0 10px;`});

        [
            {id: "downloadUnknownLingqsBtn", textContent: "Download Unknown LingQs (words + phrases)", className: "popup-button"},
            {id: "downloadUnknownLingqWordsBtn", textContent: "Download Unknown LingQ Words (1, 2, 3, 4)", className: "popup-button"},
            {id: "downloadUnknownLingqPhrasesBtn", textContent: "Download Unknown LingQ Phrases (1, 2, 3, 4)", className: "popup-button"},
            {id: "downloadKnownLingqsBtn", textContent: "Download Known LingQs (✓)", className: "popup-button"},
            {id: "downloadKnownWordsBtn", textContent: "Download Known Words ", className: "popup-button"}
        ].forEach((prop) => {
            let rowContainer = createElement("div", {className: "popup-row"});
            rowContainer.appendChild(createElement("button", prop))
            content.appendChild(rowContainer);
        });

        // Progress Bar Elements
        const progressContainer = createElement("div", {
            id: "downloadProgressContainer",
            style: "display: none;",
            className: "popup-row"
        });
        const progressText = createElement("div", {
            id: "downloadProgressText",
            style: "text-align: center; margin-bottom: 5px; font-size: 0.9em;"
        });
        const progressBar = createElement("progress", {
            id: "downloadProgressBar",
            value: "0",
            max: "100",
            style: "width: 100%; height: 20px;"
        });

        progressContainer.appendChild(progressText);
        progressContainer.appendChild(progressBar);
        content.appendChild(progressContainer);

        const buttonContainer = createElement("div", {style: "display: flex; justify-content: flex-end;", className: "popup-row"});
        const closeButton = createElement("button", {id: "closeDownloadWordsBtn", textContent: "Close", className: "popup-button"});
        buttonContainer.appendChild(closeButton);
        content.appendChild(buttonContainer);

        popup.appendChild(dragHandle);
        popup.appendChild(content);

        return popup;
    }

    function createElement(tag, props = {}) {
        const element = document.createElement(tag);
        Object.entries(props).forEach(([key, value]) => {
            if (key === "style" && typeof value === "string") {
                element.style.cssText = value;
            } else if (key === "textContent") {
                element.textContent = value;
            } else {
                element[key] = value;
            }
        });
        return element;
    }

    function addSelect(parent, id, labelText, options, selectedValue) {
        const container = createElement("div", {className: "popup-row"});
        container.appendChild(createElement("label", {htmlFor: id, textContent: labelText}));

        const select = createElement("select", {id, style: "width: 100%; margin-top: 5px; padding: 5px;"});
        options.forEach(option => {
            select.appendChild(createElement("option", {value: option.value, textContent: option.text, selected: selectedValue === option.value}));
        });

        container.appendChild(select);
        parent.appendChild(container);
        return container;
    }

    function addSlider(parent, id, labelText, valueId, value, unit, min, max, step) {
        const container = createElement("div", {className: "popup-row"});

        const label = createElement("label", { htmlFor: id });
        label.appendChild(document.createTextNode(labelText + " "));
        label.appendChild(createElement("span", { id: valueId, textContent: value }));
        if (unit) label.appendChild(document.createTextNode(unit));

        container.appendChild(label);
        container.appendChild(createElement("input", {type: "range", id, min, max, step, value, style: "width: 100%;"}));

        parent.appendChild(container);
        return container;
    }

    function addColorPicker(parent, id, labelText, value) {
        const container = createElement("div", {className: "popup-row"});
        container.appendChild(createElement("label", {htmlFor: id + "Text", textContent: labelText}));

        const flexContainer = createElement("div", {style: "display: flex; align-items: center;"});
        flexContainer.appendChild(createElement("div", {id: id + "Picker", className: "color-picker" }));
        flexContainer.appendChild(createElement("input", {type: "text", id: id + "Text", value, style: "flex-grow: 1; margin-left: 10px;"}));

        container.appendChild(flexContainer);
        parent.appendChild(container);
        return container;
    }

    function addCheckbox(parent, id, labelText, checked) {
        const container = createElement("div", {className: "popup-row"});
        const label = createElement("label", {htmlFor: id, textContent: labelText});
        const checkbox = createElement("input", {type: "checkbox", id, checked, style: "margin-left: 10px;"});

        label.style.display = "flex";
        label.style.alignItems = "center";
        container.appendChild(label);
        label.appendChild(checkbox);
        parent.appendChild(container);

        return container;
    }

    function setupSettingEventListeners(settingsButton, settingsPopup) {
        // Drag popup
        settingsButton.addEventListener("click", () => {
            settingsPopup.style.display = "block";
            initializePickrs();

            const dragHandle = document.getElementById("lingqAddonSettingsDragHandle");
            makeDraggable(settingsPopup, dragHandle);
        });

        // Toggle popup visibility
        settingsButton.addEventListener("click", () => {
            settingsPopup.style.display = "block";
        });

        // Close button
        document.getElementById("closeSettingsBtn").addEventListener("click", () => {
            settingsPopup.style.display = "none";
        });

        // Reset button
        document.getElementById("resetSettingsBtn").addEventListener("click", resetSettings);

        // Style type selector
        const styleTypeSelector = document.getElementById("styleTypeSelector");
        styleTypeSelector.addEventListener("change", (event) => {
            const selectedStyleType = event.target.value;
            storage.set("styleType", selectedStyleType);
            document.getElementById("videoSettings").style.display = selectedStyleType === "video" ? "block" : "none";
            document.getElementById("sentenceVideoSettings").style.display = selectedStyleType === "off" ? "block" : "none";
            applyStyles(selectedStyleType, document.getElementById("colorModeSelector").value);
        });

        // Color mode selector
        document.getElementById("colorModeSelector").addEventListener("change", updateColorMode);

        // Setup sliders
        setupSlider("fontSizeSlider", "fontSizeValue", "fontSize", "rem", "--font_size", (val) => `${val}rem`);
        setupSlider("lineHeightSlider", "lineHeightValue", "lineHeight", "", "--line_height", (val) => val);
        setupSlider("heightBigSlider", "heightBigValue", "heightBig", "px", "--height_big", (val) => `${val}px`);
        setupSlider("sentenceHeightSlider", "sentenceHeightValue", "sentenceHeight", "px", "--sentence_height", (val) => `${val}px`);

        // Auto Finishing checkbox
        const autoFinishingCheckbox = document.getElementById("autoFinishingCheckbox");
        autoFinishingCheckbox.addEventListener('change', (event) => {
            const checked = event.target.checked;
            storage.set("autoFinishing", checked);
            settings.autoFinishing = checked;
        });
    }

    function makeDraggable(element, handle) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        if (handle) {
            handle.onmousedown = dragMouseDown;
        } else {
            element.onmousedown = dragMouseDown;
        }

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();

            if (element.style.transform && element.style.transform.includes('translate')) {
                const rect = element.getBoundingClientRect();

                element.style.transform = 'none';
                element.style.top = rect.top + 'px';
                element.style.left = rect.left + 'px';
            }

            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();

            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;

            element.style.top = (element.offsetTop - pos2) + "px";
            element.style.left = (element.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }
    }

    function initializePickrs() {
        return new Promise((resolve) => {
            const pickrCss = createElement('link', {
                rel: 'stylesheet',
                href: 'https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/nano.min.css'
            });
            document.head.appendChild(pickrCss);

            const pickrScript = createElement('script', {
                src: 'https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js',
                onload: () => resolve() // Pass function reference directly
            });
            document.head.appendChild(pickrScript);
        }).then(() => {
            setupRGBAPickr('lingqBackgroundPicker', 'lingqBackgroundText', 'lingqBackground', '--lingq_background');
            setupRGBAPickr('lingqBorderPicker', 'lingqBorderText', 'lingqBorder', '--lingq_border');
            setupRGBAPickr('lingqBorderLearnedPicker', 'lingqBorderLearnedText', 'lingqBorderLearned', '--lingq_border_learned');
            setupRGBAPickr('blueBorderPicker', 'blueBorderText', 'blueBorder', '--blue_border');
            setupRGBAPickr('fontColorPicker', 'fontColorText', 'fontColor', '--font_color');
            setupRGBAPickr('playingUnderlinePicker', 'playingUnderlineText', 'playingUnderline', '--is_playing_underline');
        });
    }

    function setupRGBAPickr(pickerId, textId, settingKey, cssVar) {
        const pickerElement = document.getElementById(pickerId);
        const textElement = document.getElementById(textId);

        if (!pickerElement || !textElement) return;

        pickerElement.style.backgroundColor = textElement.value;

        const pickr = Pickr.create({
            el: pickerElement,
            theme: 'nano',
            useAsButton: true,
            default: textElement.value,
            components: {preview: true, opacity: true, hue: true}
        });

        pickr.on('change', (color) => {
            const rgbaColor = color.toRGBA();

            const r = Math.round(rgbaColor[0]);
            const g = Math.round(rgbaColor[1]);
            const b = Math.round(rgbaColor[2]);
            const a = rgbaColor[3];

            const roundedRGBA = `rgba(${r}, ${g}, ${b}, ${a})`;

            textElement.value = roundedRGBA;
            pickerElement.style.backgroundColor = roundedRGBA;
            document.documentElement.style.setProperty(cssVar, roundedRGBA);

            saveColorSetting(settingKey, roundedRGBA);
        });

        textElement.addEventListener('change', function () {
            const rgbaColor = this.value;

            pickr.setColor(this.value);
            saveColorSetting(settingKey, rgbaColor);
            document.documentElement.style.setProperty(cssVar, rgbaColor);
            pickerElement.style.backgroundColor = rgbaColor;
        });

        pickr.on('hide', () => {
            const rgbaColor = pickr.getColor().toRGBA().toString();
            pickerElement.style.backgroundColor = rgbaColor;
        });
    }

    // Helper function to set up slider controls
    function setupSlider(sliderId, valueId, settingKey, unit, cssVar, valueTransform) {
        const slider = document.getElementById(sliderId);
        const valueDisplay = document.getElementById(valueId);

        slider.addEventListener("input", function () {
            const value = parseFloat(this.value);
            const transformedValue = valueTransform(value);

            valueDisplay.textContent = transformedValue.toString().replace(unit, '');
            storage.set(settingKey, value);
            document.documentElement.style.setProperty(cssVar, transformedValue);
        });
    }

    // Function to save color setting with appropriate prefix
    function saveColorSetting(key, value) {
        const currentColorMode = document.getElementById("colorModeSelector").value;
        const prefix = currentColorMode === "dark" ? "dark_" : "white_";
        storage.set(prefix + key, value);
    }

    // Update color mode and related settings
    function updateColorMode(event) {
        event.stopPropagation();

        const selectedColorMode = this.value;
        const settingsPopup = document.getElementById("lingqAddonSettingsPopup");
        settingsPopup.style.backgroundColor = selectedColorMode === "dark" ? "#2a2c2e" : "#ffffff";

        storage.set("colorMode", selectedColorMode);

        const colorSettings = getColorSettings(selectedColorMode);

        updateColorInputs(colorSettings);

        document.documentElement.style.setProperty(
            "--background-color",
            selectedColorMode === "dark" ? "#2a2c2e" : "#ffffff"
        );
        updateCssColorVariables(colorSettings);

        applyStyles(document.getElementById("styleTypeSelector").value, selectedColorMode);

        updateColorPickerBackgrounds(colorSettings);
    }

    // Update all color input fields with new settings
    function updateColorInputs(colorSettings) {
        document.getElementById("fontColorText").value = colorSettings.fontColor;
        document.getElementById("lingqBackgroundText").value = colorSettings.lingqBackground;
        document.getElementById("lingqBorderText").value = colorSettings.lingqBorder;
        document.getElementById("lingqBorderLearnedText").value = colorSettings.lingqBorderLearned;
        document.getElementById("blueBorderText").value = colorSettings.blueBorder;
        document.getElementById("playingUnderlineText").value = colorSettings.playingUnderline;

        const fontColorPicker = document.getElementById("fontColorPicker");
        if (fontColorPicker) fontColorPicker.style.backgroundColor = colorSettings.fontColor;

        const playingUnderlinePicker = document.getElementById("playingUnderlinePicker");
        if (playingUnderlinePicker) playingUnderlinePicker.style.backgroundColor = colorSettings.playingUnderline;
    }

    // Update color picker backgrounds
    function updateColorPickerBackgrounds(colorSettings) {
        const pickerIds = [
            { id: "lingqBackgroundPicker", color: colorSettings.lingqBackground },
            { id: "lingqBorderPicker", color: colorSettings.lingqBorder },
            { id: "lingqBorderLearnedPicker", color: colorSettings.lingqBorderLearned },
            { id: "blueBorderPicker", color: colorSettings.blueBorder },
            { id: "fontColorPicker", color: colorSettings.fontColor },
            { id: "playingUnderlinePicker", color: colorSettings.playingUnderline }
        ];

        pickerIds.forEach(item => {
            const picker = document.getElementById(item.id);
            if (picker) {
                picker.style.backgroundColor = item.color;
            }
        });
    }

    // Update CSS color variables
    function updateCssColorVariables(colorSettings) {
        document.documentElement.style.setProperty("--font_color", colorSettings.fontColor);
        document.documentElement.style.setProperty("--lingq_background", colorSettings.lingqBackground);
        document.documentElement.style.setProperty("--lingq_border", colorSettings.lingqBorder);
        document.documentElement.style.setProperty("--lingq_border_learned", colorSettings.lingqBorderLearned);
        document.documentElement.style.setProperty("--blue_border", colorSettings.blueBorder);
        document.documentElement.style.setProperty("--is_playing_underline", colorSettings.playingUnderline);
    }

    // Reset settings to defaults
    function resetSettings() {
        if (!confirm("Reset all settings to default?")) return;

        const currentColorMode = document.getElementById("colorModeSelector").value;
        const defaultColorSettings = currentColorMode === "dark" ? defaults.darkColors : defaults.whiteColors;

        document.getElementById("styleTypeSelector").value = defaults.styleType;
        document.getElementById("fontSizeSlider").value = defaults.fontSize;
        document.getElementById("fontSizeValue").textContent = defaults.fontSize;
        document.getElementById("lineHeightSlider").value = defaults.lineHeight;
        document.getElementById("lineHeightValue").textContent = defaults.lineHeight;
        document.getElementById("heightBigSlider").value = defaults.heightBig;
        document.getElementById("heightBigValue").textContent = defaults.heightBig;
        document.getElementById("sentenceHeightSlider").value = defaults.sentenceHeight;
        document.getElementById("sentenceHeightValue").textContent = defaults.sentenceHeight;

        updateColorInputs(defaultColorSettings);
        updateColorPickerBackgrounds(defaultColorSettings);

        for (const [key, value] of Object.entries(defaults)) {
            storage.set(key, value);
        }

        const prefix = currentColorMode === "dark" ? "dark_" : "white_";
        for (const [key, value] of Object.entries(defaultColorSettings)) {
            storage.set(prefix + key, value);
        }

        applyStyles(defaults.styleType, currentColorMode);

        document.getElementById("videoSettings").style.display = defaults.styleType === "video" ? "block" : "none";
        document.getElementById("sentenceVideoSettings").style.display = defaults.styleType === "off" ? "block" : "none";

        document.documentElement.style.setProperty("--font_size", `${defaults.fontSize}rem`);
        document.documentElement.style.setProperty("--line_height", defaults.lineHeight);
        document.documentElement.style.setProperty("--height_big", `${defaults.heightBig}px`);
        document.documentElement.style.setProperty("--sentence_height", `${defaults.sentenceHeight}px`);
        updateCssColorVariables(defaultColorSettings);

        document.getElementById("autoFinishingCheckbox").checked = defaults.autoFinishing;
    }

    function setupDownloadWordsEventListeners(downloadWordsButton, downloadWordsPopup) {
        // Drag popup
        downloadWordsButton.addEventListener("click", () => {
            downloadWordsPopup.style.display = "block";

            const progressContainer = document.getElementById("downloadProgressContainer");
            if (progressContainer) progressContainer.style.display = "none";

            const dragHandle = document.getElementById("lingqDownloadWordsDragHandle");
            if (dragHandle) {
                makeDraggable(downloadWordsPopup, dragHandle);
            }
        });

        const languageCode = extractLanguageCode(document.URL);
        const pageSize = 1000;

        const setButtonsDisabled = (disabled) => {
            const buttons = downloadWordsPopup.querySelectorAll('.popup-button');
            buttons.forEach(button => {
                button.disabled = disabled;
            });
        };

        const handleDownloadButtonClick = async (url, filename, type, params = '') => {
            setButtonsDisabled(true);
            try {
                await downloadWords(url, pageSize, filename, type, params);
            } finally {
                setButtonsDisabled(false);
            }
        };

        // Download Unknown LingQs button
        document.getElementById("downloadUnknownLingqsBtn").addEventListener("click", async () => {
            await handleDownloadButtonClick(`https://www.lingq.com/api/v3/${languageCode}/cards/`, "unknown_lingqs.csv", 'lingq', '&status=0&status=1&status=2&status=3');
        });

        // Download Unknown LingQ Words button
        document.getElementById("downloadUnknownLingqWordsBtn").addEventListener("click", async () => {
            await handleDownloadButtonClick(`https://www.lingq.com/api/v3/${languageCode}/cards/`, "unknown_lingq_words.csv", 'lingq', '&status=0&status=1&status=2&status=3&phrases=false');
        });

        // Download Unknown LingQ phrases button
        document.getElementById("downloadUnknownLingqPhrasesBtn").addEventListener("click", async () => {
            await handleDownloadButtonClick(`https://www.lingq.com/api/v3/${languageCode}/cards/`, "unknown_lingq_phrases.csv", 'lingq', '&status=0&status=1&status=2&status=3&phrases=True');
        });

        // Download Known LingQs button
        document.getElementById("downloadKnownLingqsBtn").addEventListener("click", async () => {
            await handleDownloadButtonClick(`https://www.lingq.com/api/v3/${languageCode}/cards/`, "known_lingqs.csv", 'lingq', '&status=4');
        });

        // Download known words button
        document.getElementById("downloadKnownWordsBtn").addEventListener("click", async () => {
            await handleDownloadButtonClick(`https://www.lingq.com/api/v2/${languageCode}/known-words/`, "known_words.csv", "known");
        });

        // Close button
        document.getElementById("closeDownloadWordsBtn").addEventListener("click", () => {
            downloadWordsPopup.style.display = "none";
        });
    }

    function extractLanguageCode(url) {
        const regex = /https*:\/\/www\.lingq\.com\/\w+\/learn\/(\w+)\//;
        const match = url.match(regex);

        return match[1];
    }

    async function downloadWords(baseUrl, pageSize, fileName, apiType, additionalParams="") {
        const progressContainer = document.getElementById("downloadProgressContainer");
        const progressBar = document.getElementById("downloadProgressBar");
        const progressText = document.getElementById("downloadProgressText");

        if (progressContainer && progressBar && progressText) {
            progressBar.value = 0;
            progressBar.max = 100;
            progressText.textContent = "Initializing download...";
            progressContainer.style.display = "block";
        }

        const progressCallback = (currentPage, totalPages,_isDone, error_isErrorEncountered, totalCount) => {
            if (progressBar && progressText) {
                if (error_isErrorEncountered) {
                    progressText.textContent = `Error fetching page ${currentPage}: ${error_isErrorEncountered.message}`;
                    progressBar.style.backgroundColor = 'red';
                    return;
                }

                progressBar.max = totalPages;
                progressBar.value = currentPage;
                progressText.textContent = `Fetching data... Page ${currentPage} of ${totalPages} (Total items: ${totalCount || 'N/A'})`;

                if (_isDone) {
                    progressText.textContent = error_isErrorEncountered ? `Export failed: ${error_isErrorEncountered.message}` : `${totalCount} items exported`;
                }
            }
        };

        try {
            const allWords = await getAllWords(baseUrl, pageSize, apiType, additionalParams, progressCallback);

            if (!allWords || allWords.length === 0) {
                console.warn("No words found or an error occurred.");
                return;
            }

            let blob;
            const fileType = fileName.split(".")[1];

            if (fileType === 'json') {
                const dataString = JSON.stringify(allWords, null, 2);
                blob = new Blob([dataString], { type: 'application/json' });
            } else if (fileType === 'csv') {
                const headers = Object.keys(allWords[0]).join(',');
                const rows = allWords.map(item => {
                    return Object.values(item).map(value => {
                        if (typeof value === 'string') {
                            return `"${value.replace(/"/g, '""')}"`;
                        }
                        return value;
                    }).join(',');
                }).join('\n');

                const dataString = headers + '\n' + rows;
                blob = new Blob([dataString], { type: 'text/csv' });
            }

            downloadBlob(blob, fileName);
            console.log("Export completed.");
        } catch (error) {
            console.error('Error:', error);
        }
    }

    async function getAllWords(baseUrl, pageSize, apiType, additionalParams="", progressCallback = () => {}) {
        let allResults = [];
        let nextUrl = `${baseUrl}?page_size=${pageSize}&page=1${additionalParams}`;
        let currentPage = 0;
        let totalPages = 0;
        let isFirstCall = true;

        while (nextUrl) {
            try {
                const response = await fetch(nextUrl);

                if (!response.ok) {
                    throw new Error(`HTTP error! Status: ${response.status}`);
                }

                const data = await response.json();
                currentPage++;

                if (isFirstCall) {
                    isFirstCall = false;
                    totalPages = Math.ceil(data.count / pageSize);
                    console.log(`total pages: ${totalPages}`);
                }

                progressCallback(currentPage, totalPages, false, null, data.count);

                if (apiType === 'lingq') {
                    const filteredResults = data.results.map(item => ({
                        pk: item.pk,
                        term: item.term,
                        fragment: item.fragment,
                        status: item.status,
                        hint: item.hints && item.hints[0] ? item.hints[0].text : null
                    }));
                    allResults = allResults.concat(filteredResults);
                } else if (apiType === 'known') {
                    allResults = allResults.concat(data.results);
                }

                nextUrl = data.next;

                if (nextUrl) {
                    console.log("Fetched page. Next URL:", nextUrl);
                } else {
                    console.log("Finished fetching all pages");
                    progressCallback(currentPage, totalPages, true, null, data.count);
                }
            } catch (error) {
                console.error('Error fetching data:', error);
                progressCallback(currentPage, totalPages, true, error, 0);
                break;
            }
        }

        return allResults;
    }

    function downloadBlob(blob, fileName) {
        const url = URL.createObjectURL(blob);
        const a = createElement("a", {href: url, download: fileName});
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }

    function setupEventListeners() {
        document.getElementById("lingqLessonComplete").addEventListener("click", finishLesson);
    }

    function finishLesson(){
        clickElement(".reader-component > .nav--right > a");
    }

    let styleElement = null;

    // Apply styles based on current settings
    function applyStyles(styleType, colorMode) {
        const colorSettings = getColorSettings(colorMode);

        let css = generateBaseCSS(colorSettings, colorMode);
        let uiCSS = generateUICSS();
        let specificCSS = "";

        switch (colorMode) {
            case "dark":
                console.log('dark clicked');
                clickElement(".reader-themes-component > button:nth-child(5)");
                break;
            case "white":
                console.log('white clicked');
                clickElement(".reader-themes-component > button:nth-child(1)");
                break;
        }

        switch (styleType) {
            case "video":
                specificCSS = generateVideoCSS();
                break;
            case "video2":
                specificCSS = generateVideo2CSS();
                break;
            case "audio":
                specificCSS = generateAudioCSS();
                break;
            case "off":
                css = generateOffModeCSS(colorSettings);
                break;
        }

        css += specificCSS;
        css += uiCSS;

        if (styleElement) {
            styleElement.remove();
            styleElement = null;
        }

        styleElement = createElement("style", {textContent: css});
        document.querySelector("head").appendChild(styleElement);
    }

    // Generate UI-related CSS
    function generateUICSS() {
        return`
        /*Color picker*/

        .color-picker {
            width: 30px;
            height: 15px;
            border-radius: 4px;
            cursor: pointer;
        }

        .pcr-app {
            z-index: 10001 !important;
        }

        .pcr-app .pcr-interaction .pcr-result {
            color: var(--font_color) !important;
        }

        /*Popup settings*/

        #lingqAddonSettings {
            color: var(--font_color);
        }

        #lingqAddonSettingsPopup, #lingqDownloadWordsPopup {
            position: fixed;
            top: 40%;
            left: 40%;
            transform: translate(-40%, -40%);
            background-color: var(--background-color, #2a2c2e);
            color: var(--font_color, #e0e0e0);
            border: 1px solid grey;
            border-radius: 8px;
            box-shadow: 8px 8px 8px rgba(0, 0, 0, 0.2);
            z-index: 10000;
            display: none;
            width: 400px;
            max-height: 90vh;
            overflow-y: auto;
        }

        #lingqAddonSettingsDragHandle, #lingqDownloadWordsDragHandle {
            cursor: move;
            background-color: rgba(128, 128, 128, 0.2);
            padding: 8px;
            border-radius: 8px 8px 0 0;
            text-align: center;
            user-select: none;
        }

        .popup-row {
            margin: 5px 0;
        }

        .nav-button {
            background: none;
            border: none;
            cursor: pointer;
            font-size: 1.2rem;
            margin-left: 10px;
            padding: 5px;
        }

        .popup-button {
            padding: 5px 10px;
            border: 1px solid;
            border-radius: 5px;
            margin: 5px 0;
        }

        progress[value]::-webkit-progress-bar {
            border-radius: 5px;
        }

        progress[value]::-webkit-progress-value {
            border-radius: 5px;
        }
        `;
    }

    // Generate base CSS
    function generateBaseCSS(colorSettings, colorMode) {
        return `
        :root {
            --font_size: ${settings.fontSize}rem;
            --line_height: ${settings.lineHeight};

            --article_height: calc(var(--app-height) - var(--height_big) - 10px);
            --grid-layout: var(--article_height) calc(var(--height_big) - 80px) 90px;

            --font_color: ${colorSettings.fontColor};
            --lingq_background: ${colorSettings.lingqBackground};
            --lingq_border: ${colorSettings.lingqBorder};
            --lingq_border_learned: ${colorSettings.lingqBorderLearned};
            --blue_border: ${colorSettings.blueBorder};
            --is_playing_underline: ${colorSettings.playingUnderline};

            --background-color: ${colorMode === "dark" ? "#2a2c2e" : "#ffffff"}
        }

        /*header settings*/

        .main-wrapper {
            padding: 0 !important;
        }

        #main-nav {
            z-index: 1;
        }

        #main-nav > nav {
            height: 50px;
        }

        #main-nav > nav > div:nth-child(1) {
            height: 50px;
        }

        .main-header {
            pointer-events: none;
        }

        .main-header > div {
            grid-template-columns: 1fr 150px !important;
            padding-left: 400px !important;
        }

        .main-header section:nth-child(1) {
            display: none;
        }

        .main-header section {
            pointer-events: auto;
            z-index: 1;
        }

        .main-header svg {
            width: 20px !important;
            height: 20px !important;
        }

        .lesson-progress-section {
            grid-template-rows: unset !important;
            grid-template-columns: unset !important;
            grid-column: 1 !important;
            pointer-events: auto;
        }

        .lesson-progress-section .rc-slider{
            grid-row: unset !important;
            grid-column: unset !important;
            width: 50% !important;
        }

        /*layout*/

        #lesson-reader {
            grid-template-rows: var(--grid-layout);
            overflow-y: hidden;
            height: auto !important;
        }

        .sentence-text {
            height: calc(var(--article_height) - 70px) !important;
        }

        .reader-container-wrapper {
            height: 100% !important;
        }

        .widget-area {
            padding-top: 50px !important;
            height: 100% !important;
        }

        .main-footer {
            grid-area: 3 / 1 / 3 / 1 !important;
            align-self: end;
            margin: 10px 0;
        }

        .main-content {
            grid-template-rows: 45px 1fr !important;
            overflow: hidden;
            align-items: anchor-center;
        }

        /*make prev/next page buttons compact*/

        .reader-component {
            grid-template-columns: 0rem 1fr 0rem !important;
            align-items: baseline;
            margin-top: 10px;
        }

        .reader-component > div > a.button > span {
            width: 0.5rem !important;
        }

        .reader-component > div > a.button > span > svg {
            width: 15px !important;
            height: 15px !important;
        }

        .loadedContent {
            padding: 0 0 5px 15px !important;;
        }

        /*video viewer*/

        .video-player {
            display: flex !important;
            justify-content: flex-end !important;
            align-items: flex-start !important;
            pointer-events: none;
        }

        .video-player > .modal-background {
            background-color: rgb(26 28 30 / 0%) !important;
        }

        .video-player > .modal-content {
            max-width: var(--width_big) !important;
            margin: var(--video_margin) !important;
            border-radius: 0.75rem !important;
        }

        .video-player .modal-section {
            display: none !important;
        }

        .video-wrapper {
            height: var(--height_big) !important;
            overflow: hidden;
            pointer-events: auto;
        }

        /*video controller*/

        .rc-slider-rail {
            background-color: dimgrey !important;
        }

        .lingq-audio-player {
            margin-left: 10px;
        }

        .section--player.is-expanded {
            padding: 5px 0px !important;
            width: 390px !important;
            margin-left: 10px !important;
        }

        .sentence-mode-button {
            margin: 0 0 10px 0;
        }

        .player-wrapper {
            grid-template-columns: 1fr 40px !important;
            padding: 0 !important;
        }

        .audio-player {
            padding: 0 0.5rem !important;
        }

        /*font settings*/

        .reader-container {
            margin: 0 !important;
            float: left !important;
            line-height: var(--line_height) !important;
            font-size: var(--font_size) !important;
            columns: unset !important;
            overflow-y: scroll !important;
            max-width: unset !important;
        }

        .sentence-text-head {
            min-height: 4.5rem !important;
        }

        .reader-container p {
            margin-top: 0 !important;
        }

        .reader-container p span.sentence-item,
        .reader-container p .sentence {
            color: var(--font_color) !important;
        }

        .sentence.is-playing,
        .sentence.is-playing span {
            text-underline-offset: .2em !important;
            text-decoration-color: var(--is_playing_underline) !important;
        }

        /*highlightings*/

        .phrase-item {
            padding: 0 !important;
        }

        .phrase-item:not(.phrase-item-status--4, .phrase-item-status--4x2)) {
            background-color: var(--lingq_background) !important;
        }

        .phrase-item.phrase-item-status--4,
        .phrase-item.phrase-item-status--4x2 {
            background-color: rgba(0, 0, 0, 0) !important;
        }

        .phrase-cluster:not(:has(.phrase-item-status--4, .phrase-item-status--4x2)) {
            border: 1px solid var(--lingq_border) !important;
            border-radius: .25rem;
        }

        .phrase-cluster:has(.phrase-item-status--4, .phrase-item-status--4x2) {
            border: 1px solid var(--lingq_border_learned) !important;
            border-radius: .25rem;
        }

        .reader-container .sentence .lingq-word:not(.is-learned) {
            border: 1px solid var(--lingq_border) !important;
            background-color: var(--lingq_background) !important;
        }

        .reader-container .sentence .lingq-word.is-learned {
            border: 1px solid var(--lingq_border_learned) !important;
        }

        .reader-container .sentence .blue-word {
            border: 1px solid var(--blue_border) !important;
        }

        .phrase-cluster:hover,
        .phrase-created:hover {
            padding: 0 !important;
        }

        .phrase-cluster:hover .phrase-item,
        .phrase-created .phrase-item {
            padding: 0 !important;
        }

        .reader-container .sentence .selected-text {
            padding: 0 !important;
        }
        `;
    }

    // Generate Video mode CSS
    function generateVideoCSS() {
        return `
        :root {
            --width_big: calc(100vw - 424px - 10px);
            --height_big: ${settings.heightBig}px;
            --video_margin: 0 0 10px 10px !important;
        }

        .main-content {
            grid-area: 1 / 1 / 2 / 2 !important;
        }

        .widget-area {
            grid-area: 1 / 2 / 3 / 2 !important;
        }

        .main-footer {
            grid-area: 3 / 2 / 4 / 3 !important;
            align-self: end;
        }
        `;
    }

    // Generate Video2 mode CSS
    function generateVideo2CSS() {
        return `
        :root {
            --width_big: calc(50vw - 217px);
            --height_big: calc(100vh - 80px);

            --grid-layout: var(--article_height) 90px;
            --video_margin: 0 10px 10px 10px !important;
            --article_height: calc(var(--app-height) - 85px);
        }

        .page.reader-page.has-widget-fixed:not(.is-edit-mode):not(.workspace-sentence-reviewer) {
            grid-template-columns: 1fr 424px 1fr;
        }

        .main-content {
            grid-area: 1 / 1 / 2 / 2 !important;
        }

        .widget-area {
            grid-area: 1 / 2 / 2 / 3 !important;
        }

        .main-footer {
            grid-area: 2 / 2 / 3 / 3 !important;
            align-self: end;
        }

        .modal-container .modls {
            align-items: end;
        }
        `;
    }

    // Generate Audio mode CSS
    function generateAudioCSS() {
        return `
        :root {
            --height_big: 60px;
        }

        .main-content {
            grid-area: 1 / 1 / 2 / 2 !important;
        }

        .widget-area {
            grid-area: 1 / 2 / 2 / 2 !important;
        }
        `;
    }

    // Generate Off mode CSS
    function generateOffModeCSS(colorSettings) {
        return `
        :root {
            --width_small: 440px;
            --height_small: 260px;
            --sentence_height: ${settings.sentenceHeight}px;
            --right_pos: 0.5%;
            --bottom_pos: 5.5%;
        }

        /*video player*/

        .video-player.is-minimized .video-wrapper,
        .sent-video-player.is-minimized .video-wrapper {
            height: var(--height_small);
            width: var(--width_small);
            overflow: auto;
            resize: both;
        }

        .video-player.is-minimized .modal-content,
        .sent-video-player.is-minimized .modal-content {
            max-width: calc(var(--width_small)* 3);
            margin-bottom: 0;
        }

        .video-player.is-minimized,
        .sent-video-player.is-minimized {
            left: auto;
            top: auto;
            right: var(--right_pos);
            bottom: var(--bottom_pos);
            z-index: 99999999;
            overflow: visible
        }

        /*sentence mode video player*/
        .loadedContent:has(#sentence-video-player-portal) {
            grid-template-rows: var(--sentence_height) auto auto 1fr !important;
        }

        #sentence-video-player-portal .video-section {
            width: 100% !important;
            max-width: none !important;
        }

        #sentence-video-player-portal .video-wrapper {
            height: 100% !important;
            max-height: none !important;
        }

        #sentence-video-player-portal div:has(> iframe) {
            height: 100% !important;
        }
        `;
    }

    // Keyboard shortcuts and other functionality
    function setupKeyboardShortcuts() {
        function preventPropagation(event){
            event.preventDefault();
            event.stopPropagation();
        }

        document.addEventListener("keydown", function (event) {
            const targetElement = event.target;
            const isTextInput = targetElement.type === "text" || targetElement.type === "textarea";
            const withoutModifierKeys = !event.ctrlKey && !event.shiftKey && !event.altKey;
            const eventKey = event.key.toLowerCase();
            if (isTextInput) {
                if ((eventKey == 'enter' || eventKey == 'esc') && withoutModifierKeys) {
                    preventPropagation(event);
                    event.target.blur();
                } else {
                    return;
                }
            }

            const shortcuts = {
                'q': () => clickElement(".modal-section > div > button:nth-child(2)"), // video full screen toggle
                'w': () => clickElement(".audio-player--controllers > div:nth-child(1) > a"), // 5 sec Backward
                'e': () => clickElement(".audio-player--controllers > div:nth-child(2) > a"), // 5 sec Forward
                'r': () => document.dispatchEvent(new KeyboardEvent("keydown", { key: "k" })), // Make word Known
                't': () => clickElement(".dictionary-resources > a:nth-last-child(1)"), // Open Translator
                '`': () => focusElement(".reference-input-text"), // Move cursor to reference input
                'd': () => clickElement(".dictionary-resources > a:nth-child(1)"), // Open Dictionary
                'f': () => clickElement(".dictionary-resources > a:nth-child(1)"), // Open Dictionary
                'c': () => copySelectedText() // Copy selected text
            };

            if (shortcuts[eventKey] && withoutModifierKeys) {
                preventPropagation(event);
                shortcuts[eventKey]();
            }
        }, true);
    }

    // Helper function to click an element
    function clickElement(selector) {
        const element = document.querySelector(selector);
        if (element) element.click();
    }

    // Helper function to focus an element
    function focusElement(selector) {
        const element = document.querySelector(selector);
        if (element) {
            element.focus();
            element.setSelectionRange(element.value.length, element.value.length);
        }
    }

    // Helper function to copy selected text
    function copySelectedText() {
        const selected_text = document.querySelector(".reference-word");
        if (selected_text) {
            navigator.clipboard.writeText(selected_text.textContent);
        }
    }

    // Custom embedded player
    function setupYoutubePlayerCustomization() {
        function replaceNoCookie() {
            document.querySelectorAll("iframe").forEach(function (iframe) {
                let src = iframe.getAttribute("src");
                if (src && src.includes("disablekb=1")) {
                    src = src.replace("disablekb=1", "disablekb=0"); // keyboard controls are enabled
                    src = src + "&cc_load_policy=1"; // caption is shown by default
                    src = src + "&controls=0"; // player controls do not display in the player
                    iframe.setAttribute("src", src);
                }
            });
        }

        function setupSliderObserver() {
            const sliderContainer = createElement("div", {className: "rc-slider rc-slider-horizontal"});
            const videoSliderRail = createElement("div", {className: "rc-slider-rail"});
            const videoSliderTrack = createElement("div", {className: "rc-slider-track"});
            sliderContainer.appendChild(videoSliderRail);
            sliderContainer.appendChild(videoSliderTrack);
            document.querySelector(".modal-content > div").appendChild(sliderContainer);

            const sliderTrack = document.querySelector('.audio-player--progress .rc-slider-track');
            const sliderObserver = new MutationObserver(function (mutationsList) {
                for (const mutation of mutationsList) {
                    if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
                        videoSliderTrack.style.cssText = sliderTrack.style.cssText;

                        const isLessonFinished = videoSliderTrack.style.width  === "100%";
                        if (isLessonFinished && settings.autoFinishing) {
                            finishLesson();
                        }
                    }
                }
            });

            sliderObserver.observe(sliderTrack, {attributes: true, attributeFilter: ['style']});
            console.log('Observer started for rc-slider-track');
        }

        const iframeObserver = new MutationObserver(function (mutationsList) {
            for (const mutation of mutationsList) {
                if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
                    mutation.addedNodes.forEach((node) => {
                        if (node.nodeName === "IFRAME") {
                            replaceNoCookie();
                            clickElement('.modal-section.modal-section--head button[title="Expand"]');
                            setupSliderObserver();
                        }
                    });
                }
            }
        });

        iframeObserver.observe(document.body, {childList: true, subtree: true, attributes: true, attributeFilter: ["src"]});
    }

    // Scroll customization
    function setupScrollCustomization() {
        setTimeout(() => {
            const readerContainer = document.querySelector(".reader-container");
            if (readerContainer) {
                readerContainer.addEventListener("wheel", (event) => {
                    event.preventDefault();
                    const delta = event.deltaY;
                    const scrollAmount = 0.3;
                    readerContainer.scrollTop += delta * scrollAmount;
                });
            }
        }, 3000);
    }

    // Focus on playing sentence
    function setupSentenceFocus() {
        function focusPlayingSentence() {
            const playingSentence = document.querySelector(".sentence.is-playing");
            if (playingSentence) {
                /*
                playingSentence.scrollIntoView({
                    behavior: "smooth",
                    block: "center"
                });
                */
                const scrolling_div = document.querySelector(".reader-container")
                scrolling_div.scrollTop = playingSentence.offsetTop + Math.floor(playingSentence.offsetHeight / 2) - Math.floor(scrolling_div.offsetHeight / 2);

            }
        }

        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (
                    mutation.type === "attributes" &&
                    mutation.attributeName === "class" &&
                    mutation.target.classList.contains("sentence")
                ) {
                    focusPlayingSentence();
                }
            });
        });

        const container = document.querySelector(".sentence-text");
        if (container) {
            observer.observe(container, {
                attributes: true,
                subtree: true
            });
        }
    }

    function sortLibrary() {
        function waitForLibrary(selector) {
            const observer = new MutationObserver((mutations, obs) => {
                const dropdownItems = document.querySelectorAll(selector);
                if (dropdownItems.length) {
                    // Setup library sort event listener
                    dropdownItems.forEach((item, index) => {
                        item.addEventListener('click', () => {
                            console.log(`Clicked sort option: ${index}`);
                            storage.set('librarySortOption', index);
                            settings.librarySortOption = index;
                        });
                    });

                    // Change sort by the setting
                    dropdownItems[settings.librarySortOption].click();
                    obs.disconnect();
                } else {
                    console.log("Dropdown item not found yet, trying again");
                    setTimeout(() => {waitForLibrary(selector);}, 1000);
                }
            });

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

        waitForLibrary('.library-section > .list-header .tw-dropdown--item');
    }

    function fixBugs() {
        const css = `
        .toasts {
            height: fit-content;
        }
        `;
        const cssElement = createElement("style", {textContent: css});
        document.querySelector("head").appendChild(cssElement);
    }

    // Initialize everything
    function init() {
        fixBugs();

        if (document.URL.includes("reader")) {
            createUI();
            applyStyles(settings.styleType, settings.colorMode);
            setupKeyboardShortcuts();
            setupYoutubePlayerCustomization();
            setupScrollCustomization();
            setupSentenceFocus();
        }
        if (document.URL.includes("library")) {
            sortLibrary();

        }
    }

    // Start the script
    init();
})();