Google Search Custom Sidebar

Customizable Google Search sidebar: quick filters (lang, time, filetype, country, date), site search, Verbatim & Personalization tools.

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Google Search Custom Sidebar
// @name:zh-TW   Google 搜尋自訂側邊欄
// @name:ja      Google検索カスタムサイドバー
// @namespace    https://greasyfork.org/en/users/1467948-stonedkhajiit
// @version      0.0.1
// @description  Customizable Google Search sidebar: quick filters (lang, time, filetype, country, date), site search, Verbatim & Personalization tools.
// @description:zh-TW Google 搜尋自訂側邊欄:快速篩選(語言、時間、檔案類型、國家、日期)、站內搜尋、一字不差與個人化工具。
// @description:ja Google検索カスタムサイドバー:高速フィルター(言語,期間,ファイル形式,国,日付)、サイト検索、完全一致検索とパーソナライズツール。
// @match        https://www.google.com/search*
// @include      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?q=[^&]+(?:&.+)?$/
// @exclude      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?tbm=isch(?:&.+)?$/
// @exclude      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?udm=2(?:&.+)?$/
// @exclude      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?tbm=shop(?:&.+)?$/
// @exclude      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?udm=28(?:&.+)?$/
// @exclude      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?tbm=bks(?:&.+)?$/
// @exclude      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?tbm=flm(?:&.+)?$/
// @exclude      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?tbm=fin(?:&.+)?$/
// @exclude      /^https:\/\/(?:ipv4|ipv6|www)\.google\.(?:[a-z\.]+)\/search\?(?:.+&)?tbm=lcl(?:&.+)?$/
// @icon         https://www.google.com/favicon.ico
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_deleteValue
// @run-at       document-idle
// @author       StonedKhajiit
// @license      MIT
// @require      https://update.greasyfork.org/scripts/535624/1586990/Google%20Search%20Custom%20Sidebar%20-%20i18n.js
// @require      https://update.greasyfork.org/scripts/535625/1586991/Google%20Search%20Custom%20Sidebar%20-%20Styles.js
// ==/UserScript==

(function() {
    'use strict';

    // --- Constants and Configuration ---
    const SCRIPT_INTERNAL_NAME = 'GoogleSearchCustomSidebar';
    const SCRIPT_VERSION = '0.0.1';
    const LOG_PREFIX = `[${SCRIPT_INTERNAL_NAME} v${SCRIPT_VERSION}]`;

    const DEFAULT_SECTION_ORDER = [ 'sidebar-section-language', 'sidebar-section-time', 'sidebar-section-filetype', 'sidebar-section-country', 'sidebar-section-date-range', 'sidebar-section-site-search', 'sidebar-section-tools' ];

    const defaultSettings = {
        sidebarPosition: { left: 0, top: 80 },
        sectionStates: {},
        theme: 'system',
        hoverMode: false,
        idleOpacity: 0.8,
        sidebarWidth: 135,
        fontSize: 12.5,
        headerIconSize: 16,
        verticalSpacingMultiplier: 0.5,
        interfaceLanguage: 'auto',
        visibleSections: {
            'sidebar-section-language': true, 'sidebar-section-time': true, 'sidebar-section-filetype': true,
            'sidebar-section-country': true, 'sidebar-section-date-range': true, 'sidebar-section-site-search': true,
            'sidebar-section-tools': true
        },
        sectionDisplayMode: 'remember',
        accordionMode: false,
        resetButtonLocation: 'topBlock',
        verbatimButtonLocation: 'header',
        advancedSearchLinkLocation: 'header',
        personalizationButtonLocation: 'tools',
        countryDisplayMode: 'iconAndText',
        customLanguages: [], customTimeRanges: [], customFiletypes: [], customCountries: [],
        favoriteSites: [
            { text: 'Wikipedia (EN)', url: 'en.wikipedia.org' }, { text: 'Stack Overflow', url: 'stackoverflow.com' },
            { text: 'GitHub', url: 'github.com' }, { text: 'Greasy Fork', url: 'greasyfork.org' },
            { text: 'Bluesky', url: 'bsky.app' }, { text: 'X.com', url: 'x.com' },
            { text: 'Reddit', url: 'reddit.com' }, { text: 'IMDb', url: 'imdb.com' },
            { text: 'Steam', url: 'store.steampowered.com' }, { text: 'Last.fm', url: 'last.fm' },
            { text: 'Metacritic', url: 'metacritic.com' }, { text: 'TMDb', url: 'themoviedb.org' },
            { text: 'Hacker News', url: 'news.ycombinator.com' }
        ],
        sidebarCollapsed: false,
        draggableHandleEnabled: true,
        enabledPredefinedOptions: {
            language: ['lang_en'], country: ['countryUS'],
            time: ['d', 'w', 'm', 'y', 'h'],
            filetype: ['pdf', 'docx', 'doc', 'xlsx', 'xls', 'pptx', 'ppt']
        },
        sidebarSectionOrder: [...DEFAULT_SECTION_ORDER]
    };

    let sidebar = null, systemThemeMediaQuery = null;
    const MIN_SIDEBAR_TOP_POSITION = 5;
    let debouncedSaveSettings;
    let globalMessageTimeout = null;

    const IDS = { /* ... (same as before) ... */
        SIDEBAR: 'customizable-search-sidebar', SETTINGS_OVERLAY: 'settings-overlay', SETTINGS_WINDOW: 'settings-window',
        COLLAPSE_BUTTON: 'sidebar-collapse-button', SETTINGS_BUTTON: 'open-settings-button',
        TOOL_RESET_BUTTON: 'tool-reset-button', TOOL_VERBATIM: 'tool-verbatim', TOOL_PERSONALIZE: 'tool-personalize-search',
        FIXED_TOP_BUTTONS: 'sidebar-fixed-top-buttons',
        SETTINGS_MESSAGE_BAR: 'gscs-settings-message-bar',
        SETTING_WIDTH: 'setting-sidebar-width', SETTING_FONT_SIZE: 'setting-font-size', SETTING_HEADER_ICON_SIZE: 'setting-header-icon-size',
        SETTING_VERTICAL_SPACING: 'setting-vertical-spacing', SETTING_INTERFACE_LANGUAGE: 'setting-interface-language',
        SETTING_SECTION_MODE: 'setting-section-display-mode', SETTING_ACCORDION: 'setting-accordion-mode',
        SETTING_DRAGGABLE: 'setting-draggable-handle', SETTING_RESET_LOCATION: 'setting-reset-button-location',
        SETTING_VERBATIM_LOCATION: 'setting-verbatim-button-location', SETTING_ADV_SEARCH_LOCATION: 'setting-adv-search-link-location',
        SETTING_PERSONALIZE_LOCATION: 'setting-personalize-button-location',
        SETTING_COUNTRY_DISPLAY_MODE: 'setting-country-display-mode', SETTING_THEME: 'setting-theme',
        SETTING_HOVER: 'setting-hover-mode', SETTING_OPACITY: 'setting-idle-opacity',
        TAB_PANE_GENERAL: 'tab-pane-general', TAB_PANE_APPEARANCE: 'tab-pane-appearance', TAB_PANE_FEATURES: 'tab-pane-features', TAB_PANE_CUSTOM: 'tab-pane-custom',
        SITES_LIST: 'custom-sites-list', LANG_LIST: 'custom-languages-list', TIME_LIST: 'custom-time-ranges-list',
        FT_LIST: 'custom-filetypes-list', COUNTRIES_LIST: 'custom-countries-list',
        NEW_SITE_NAME: 'new-site-name', NEW_SITE_URL: 'new-site-url', ADD_SITE_BTN: 'add-site-button',
        NEW_LANG_TEXT: 'new-lang-text', NEW_LANG_VALUE: 'new-lang-value', ADD_LANG_BTN: 'add-lang-button',
        NEW_TIME_TEXT: 'new-timerange-text', NEW_TIME_VALUE: 'new-timerange-value', ADD_TIME_BTN: 'add-timerange-button',
        NEW_FT_TEXT: 'new-ft-text', NEW_FT_VALUE: 'new-ft-value', ADD_FT_BTN: 'add-ft-button',
        NEW_COUNTRY_TEXT: 'new-country-text', NEW_COUNTRY_VALUE: 'new-country-value', ADD_COUNTRY_BTN: 'add-country-button',
        DATE_MIN: 'date-min', DATE_MAX: 'date-max', DATE_RANGE_ERROR_MSG: 'date-range-error-msg',
        SIDEBAR_SECTION_ORDER_LIST: 'sidebar-section-order-list',
        NOTIFICATION_CONTAINER: 'gscs-notification-container'
    };
    const CSS = { /* ... (same as before) ... */
        SIDEBAR_COLLAPSED: 'sidebar-collapsed', SIDEBAR_HEADER: 'sidebar-header', SIDEBAR_CONTENT_WRAPPER: 'sidebar-content-wrapper',
        DRAG_HANDLE: 'sidebar-drag-handle', SETTINGS_BUTTON: 'sidebar-settings-button', HEADER_BUTTON: 'sidebar-header-button',
        SIDEBAR_SECTION: 'sidebar-section', FIXED_TOP_BUTTON_ITEM: 'fixed-top-button-item', SECTION_TITLE: 'section-title',
        SECTION_CONTENT: 'section-content', COLLAPSED: 'collapsed', FILTER_OPTION: 'filter-option', SELECTED: 'selected',
        DATE_INPUT_LABEL: 'date-input-label', DATE_INPUT: 'date-input', TOOL_BUTTON: 'tool-button', ACTIVE: 'active',
        CUSTOM_LIST: 'custom-list', ITEM_CONTROLS: 'item-controls', EDIT_CUSTOM_ITEM: 'edit-custom-item',
        DELETE_CUSTOM_ITEM: 'delete-custom-item', CUSTOM_LIST_INPUT_GROUP: 'custom-list-input-group',
        ADD_CUSTOM_BUTTON: 'add-custom-button', SETTINGS_HEADER: 'settings-header', SETTINGS_CLOSE_BTN: 'settings-close-button',
        SETTINGS_TABS: 'settings-tabs', TAB_BUTTON: 'tab-button', SETTINGS_TAB_CONTENT: 'settings-tab-content',
        TAB_PANE: 'tab-pane', SETTING_ITEM: 'setting-item', INLINE_LABEL: 'inline', SETTINGS_FOOTER: 'settings-footer',
        SAVE_BUTTON: 'save-button', CANCEL_BUTTON: 'cancel-button', RESET_BUTTON: 'reset-button',
        LIGHT_THEME: 'light-theme', DARK_THEME: 'dark-theme',
        SIMPLE_ITEM: 'simple', RANGE_VALUE: 'range-value', RANGE_HINT: 'setting-range-hint', SECTION_ORDER_LIST: 'section-order-list',
        INPUT_ERROR_MESSAGE: 'input-error-message', ERROR_VISIBLE: 'error-visible', INPUT_HAS_ERROR: 'input-has-error',
        DATE_RANGE_ERROR_MSG: 'date-range-error-message',
        MESSAGE_BAR: 'gscs-message-bar', MSG_INFO: 'gscs-msg-info', MSG_SUCCESS: 'gscs-msg-success',
        MSG_WARNING: 'gscs-msg-warning', MSG_ERROR: 'gscs-msg-error', MANAGE_CUSTOM_BUTTON: 'manage-custom-button',
        NOTIFICATION: 'gscs-notification',
        NTF_INFO: 'gscs-ntf-info', NTF_SUCCESS: 'gscs-ntf-success',
        NTF_WARNING: 'gscs-ntf-warning', NTF_ERROR: 'gscs-ntf-error'
    };
    const DATA_ATTR = { /* ... (same as before) ... */
        FILTER_TYPE: 'filterType', FILTER_VALUE: 'filterValue', SITE_URL: 'siteUrl', SECTION_ID: 'sectionId',
        LIST_ID: 'listId', INDEX: 'index', LISTENER_ATTACHED: 'listenerAttached', TAB: 'tab', MANAGE_TYPE: 'managetype'
    };
    const STORAGE_KEY = 'googleSearchCustomSidebarSettings_v1';
    const SVG_ICONS = { /* ... (same as before, including single personalization icon) ... */
        chevronLeft: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>`,
        chevronRight: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>`,
        settings: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06-.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>`,
        reset: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
        verbatim: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(-4.3875 -3.2375) scale(1.15)"><path d="M6 17.5c0 1.5 1.5 2.5 3 2.5h1.5c1.5 0 3-1 3-2.5V9c0-1.5-1.5-2.5-3-2.5H9C7.5 6.5 6 7.5 6 9v8.5z"/><path d="M15 17.5c0 1.5 1.5 2.5 3 2.5h1.5c1.5 0 3-1 3-2.5V9c0-1.5-1.5-2.5-3-2.5H18c-1.5 0-3 1-3 2.5v8.5z"/></g></svg>`,
        magnifyingGlass: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>`,
        close: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`,
        edit: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>`,
        delete: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>`,
        add: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>`,
        update: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`,
        personalization: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>`,
    };
    const PREDEFINED_OPTIONS = { /* ... (same as before) ... */
        language: [ { textKey: 'predefined_lang_en', value: 'lang_en' }, { textKey: 'predefined_lang_ja', value: 'lang_ja' }, { textKey: 'predefined_lang_ko', value: 'lang_ko' }, { textKey: 'predefined_lang_fr', value: 'lang_fr' }, { textKey: 'predefined_lang_de', value: 'lang_de' }, { textKey: 'predefined_lang_es', value: 'lang_es' }, { textKey: 'predefined_lang_it', value: 'lang_it' }, { textKey: 'predefined_lang_pt', value: 'lang_pt' }, { textKey: 'predefined_lang_ru', value: 'lang_ru' }, { textKey: 'predefined_lang_ar', value: 'lang_ar' }, { textKey: 'predefined_lang_hi', value: 'lang_hi' }, { textKey: 'predefined_lang_nl', value: 'lang_nl' }, { textKey: 'predefined_lang_tr', value: 'lang_tr' }, { textKey: 'predefined_lang_vi', value: 'lang_vi' }, { textKey: 'predefined_lang_th', value: 'lang_th' }, { textKey: 'predefined_lang_id', value: 'lang_id' }, { textKey: 'predefined_lang_zh_tw', value: 'lang_zh-TW' }, { textKey: 'predefined_lang_zh_cn', value: 'lang_zh-CN' }, { textKey: 'predefined_lang_zh_all', value: 'lang_zh-TW|lang_zh-CN' }, ],
        country: [ { textKey: 'predefined_country_us', value: 'countryUS' }, { textKey: 'predefined_country_gb', value: 'countryGB' }, { textKey: 'predefined_country_ca', value: 'countryCA' }, { textKey: 'predefined_country_au', value: 'countryAU' }, { textKey: 'predefined_country_de', value: 'countryDE' }, { textKey: 'predefined_country_fr', value: 'countryFR' }, { textKey: 'predefined_country_jp', value: 'countryJP' }, { textKey: 'predefined_country_kr', value: 'countryKR' }, { textKey: 'predefined_country_cn', value: 'countryCN' }, { textKey: 'predefined_country_in', value: 'countryIN' }, { textKey: 'predefined_country_br', value: 'countryBR' }, { textKey: 'predefined_country_mx', value: 'countryMX' }, { textKey: 'predefined_country_es', value: 'countryES' }, { textKey: 'predefined_country_it', value: 'countryIT' }, { textKey: 'predefined_country_ru', value: 'countryRU' }, { textKey: 'predefined_country_nl', value: 'countryNL' }, { textKey: 'predefined_country_sg', value: 'countrySG' }, { textKey: 'predefined_country_hk', value: 'countryHK' }, { textKey: 'predefined_country_tw', value: 'countryTW' }, { textKey: 'predefined_country_my', value: 'countryMY' }, { textKey: 'predefined_country_vn', value: 'countryVN' }, { textKey: 'predefined_country_ph', value: 'countryPH' }, { textKey: 'predefined_country_th', value: 'countryTH' }, { textKey: 'predefined_country_za', value: 'countryZA' }, { textKey: 'predefined_country_tr', value: 'countryTR' }, ],
        time: [ { textKey: 'predefined_time_h', value: 'h' }, { textKey: 'predefined_time_h2', value: 'h2' }, { textKey: 'predefined_time_h6', value: 'h6' }, { textKey: 'predefined_time_h12', value: 'h12' }, { textKey: 'predefined_time_d', value: 'd' }, { textKey: 'predefined_time_d2', value: 'd2' }, { textKey: 'predefined_time_d3', value: 'd3' }, { textKey: 'predefined_time_w', value: 'w' }, { textKey: 'predefined_time_m', value: 'm' }, { textKey: 'predefined_time_y', value: 'y' }, ],
        filetype: [ { textKey: 'predefined_filetype_pdf', value: 'pdf' }, { textKey: 'predefined_filetype_docx', value: 'docx' }, { textKey: 'predefined_filetype_doc', value: 'doc' }, { textKey: 'predefined_filetype_xlsx', value: 'xlsx' }, { textKey: 'predefined_filetype_xls', value: 'xls' }, { textKey: 'predefined_filetype_pptx', value: 'pptx' }, { textKey: 'predefined_filetype_ppt', value: 'ppt' }, { textKey: 'predefined_filetype_txt', value: 'txt' }, { textKey: 'predefined_filetype_rtf', value: 'rtf' }, { textKey: 'predefined_filetype_html', value: 'html' }, { textKey: 'predefined_filetype_htm', value: 'htm' }, { textKey: 'predefined_filetype_xml', value: 'xml' }, { textKey: 'predefined_filetype_jpg', value: 'jpg' }, { textKey: 'predefined_filetype_png', value: 'png' }, { textKey: 'predefined_filetype_gif', value: 'gif' }, { textKey: 'predefined_filetype_svg', value: 'svg' }, { textKey: 'predefined_filetype_bmp', value: 'bmp' }, { textKey: 'predefined_filetype_js', value: 'js' }, { textKey: 'predefined_filetype_css', value: 'css' }, { textKey: 'predefined_filetype_py', value: 'py' }, { textKey: 'predefined_filetype_java', value: 'java' }, { textKey: 'predefined_filetype_cpp', value: 'cpp' }, { textKey: 'predefined_filetype_cs', value: 'cs' }, { textKey: 'predefined_filetype_kml', value: 'kml'}, { textKey: 'predefined_filetype_kmz', value: 'kmz'}, ]
    };
    const ALL_SECTION_DEFINITIONS = [ /* ... (same as before) ... */ { id: 'sidebar-section-language', type: 'filter', titleKey: 'section_language', scriptDefined: [{textKey:'filter_any_language',v:''}], param: 'lr', predefinedOptionsKey: 'language', customItemsKey: 'customLanguages' }, { id: 'sidebar-section-time', type: 'filter', titleKey: 'section_time', scriptDefined: [{textKey:'filter_any_time',v:''}], param: 'qdr', predefinedOptionsKey: 'time', customItemsKey: 'customTimeRanges' }, { id: 'sidebar-section-filetype', type: 'filter', titleKey: 'section_filetype', scriptDefined: [{ textKey: 'filter_any_format', v: '' }], param: 'filetype', predefinedOptionsKey: 'filetype', customItemsKey: 'customFiletypes' }, { id: 'sidebar-section-country', type: 'filter', titleKey: 'section_country', scriptDefined: [{textKey:'filter_any_country',v:''}], param: 'cr', predefinedOptionsKey: 'country', customItemsKey: 'customCountries' }, { id: 'sidebar-section-date-range', type: 'date', titleKey: 'section_date_range' }, { id: 'sidebar-section-site-search', type: 'site', titleKey: 'section_site_search' }, { id: 'sidebar-section-tools', type: 'tools', titleKey: 'section_tools' } ];

    const LocalizationService = (function() { /* ... (LocalizationService with updated 'en' strings, as provided in the previous response) ... */
        const builtInTranslations = {
            'en': {
                scriptName: 'Google Search Custom Sidebar', settingsTitle: 'Google Search Custom Sidebar Settings', manageOptionsTitle: 'Manage Options', manageSitesTitle: 'Manage Favorite Sites', manageLanguagesTitle: 'Manage Custom Languages', manageCountriesTitle: 'Manage Countries/Regions', manageTimeRangesTitle: 'Manage Time Ranges', manageFileTypesTitle: 'Manage File Types', section_language: 'Language', section_time: 'Time', section_filetype: 'File Type', section_country: 'Country/Region', section_date_range: 'Date Range', section_site_search: 'Site Search', section_tools: 'Tools', filter_any_language: 'Any Language', filter_any_time: 'Any Time', filter_any_format: 'Any Format', filter_any_country: 'Any Country/Region', filter_clear_site_search: 'Clear Site Search', filter_clear_tooltip_suffix: '(Clear)', predefined_lang_zh_tw: 'Traditional Chinese', predefined_lang_zh_cn: 'Simplified Chinese', predefined_lang_zh_all: 'All Chinese', predefined_lang_en: 'English', predefined_lang_ja: 'Japanese', predefined_lang_ko: 'Korean', predefined_lang_fr: 'French', predefined_lang_de: 'German', predefined_lang_es: 'Spanish', predefined_lang_it: 'Italian', predefined_lang_pt: 'Portuguese', predefined_lang_ru: 'Russian', predefined_lang_ar: 'Arabic', predefined_lang_hi: 'Hindi', predefined_lang_nl: 'Dutch', predefined_lang_tr: 'Turkish', predefined_lang_vi: 'Vietnamese', predefined_lang_th: 'Thai', predefined_lang_id: 'Indonesian', predefined_country_tw: '🇹🇼 Taiwan', predefined_country_jp: '🇯🇵 Japan', predefined_country_kr: '🇰🇷 South Korea', predefined_country_cn: '🇨🇳 China', predefined_country_hk: '🇭🇰 Hong Kong', predefined_country_sg: '🇸🇬 Singapore', predefined_country_my: '🇲🇾 Malaysia', predefined_country_vn: '🇻🇳 Vietnam', predefined_country_ph: '🇵🇭 Philippines', predefined_country_th: '🇹🇭 Thailand', predefined_country_us: '🇺🇸 United States', predefined_country_ca: '🇨🇦 Canada', predefined_country_br: '🇧🇷 Brazil', predefined_country_mx: '🇲🇽 Mexico', predefined_country_gb: '🇬🇧 United Kingdom', predefined_country_de: '🇩🇪 Germany', predefined_country_fr: '🇫🇷 France', predefined_country_it: '🇮🇹 Italy', predefined_country_es: '🇪🇸 Spain', predefined_country_ru: '🇷🇺 Russia', predefined_country_nl: '🇳🇱 Netherlands', predefined_country_au: '🇦🇺 Australia', predefined_country_in: '🇮🇳 India', predefined_country_za: '🇿🇦 South Africa', predefined_country_tr: '🇹🇷 Turkey', predefined_time_h: 'Past hour', predefined_time_h2: 'Past 2 hours', predefined_time_h6: 'Past 6 hours', predefined_time_h12: 'Past 12 hours', predefined_time_d: 'Past 24 hours', predefined_time_d2: 'Past 2 days', predefined_time_d3: 'Past 3 days', predefined_time_w: 'Past week', predefined_time_m: 'Past month', predefined_time_y: 'Past year', predefined_filetype_pdf: 'PDF', predefined_filetype_docx: 'Word (docx)', predefined_filetype_doc: 'Word (doc)', predefined_filetype_xlsx: 'Excel (xlsx)', predefined_filetype_xls: 'Excel (xls)', predefined_filetype_pptx: 'PowerPoint (pptx)', predefined_filetype_ppt: 'PowerPoint (ppt)', predefined_filetype_txt: 'Plain Text', predefined_filetype_rtf: 'Rich Text Format', predefined_filetype_html: 'Web Page (html)', predefined_filetype_htm: 'Web Page (htm)', predefined_filetype_xml: 'XML', predefined_filetype_jpg: 'JPEG Image', predefined_filetype_png: 'PNG Image', predefined_filetype_gif: 'GIF Image', predefined_filetype_svg: 'SVG Image', predefined_filetype_bmp: 'BMP Image', predefined_filetype_js: 'JavaScript', predefined_filetype_css: 'CSS', predefined_filetype_py: 'Python', predefined_filetype_java: 'Java', predefined_filetype_cpp: 'C++', predefined_filetype_cs: 'C#', predefined_filetype_kml: 'Google Earth (kml)', predefined_filetype_kmz: 'Google Earth (kmz)',
                tool_reset_filters: 'Reset Filters', tool_verbatim_search: 'Verbatim Search', tool_advanced_search: 'Advanced Search', tool_apply_date: 'Apply Dates',
                tool_personalization_toggle: 'Personalization',
                link_advanced_search_title: 'Open Google Advanced Search page', tooltip_site_search: 'Search within {siteUrl}', tooltip_clear_site_search: 'Remove site: restriction', tooltip_toggle_personalization_on: 'Click to turn Personalization ON (Results tailored to you)', tooltip_toggle_personalization_off: 'Click to turn Personalization OFF (More generic results)', settings_tab_general: 'General', settings_tab_appearance: 'Appearance', settings_tab_features: 'Features', settings_tab_custom: 'Custom', settings_close_button_title: 'Close', settings_interface_language: 'Interface Language:', settings_language_auto: 'Auto (Browser Default)', settings_section_mode: 'Section Collapse Mode:', settings_section_mode_remember: 'Remember State', settings_section_mode_expand: 'Expand All', settings_section_mode_collapse: 'Collapse All', settings_accordion_mode: 'Accordion Mode (only when "Remember State" is active)', settings_enable_drag: 'Enable Dragging', settings_reset_button_location: 'Reset Button Location:', settings_verbatim_button_location: 'Verbatim Button Location:', settings_adv_search_location: '"Advanced Search" Link Location:', settings_personalize_button_location: 'Personalization Button Location:', settings_location_tools: 'Tools Section', settings_location_top: 'Top Block', settings_location_header: 'Sidebar Header', settings_location_hide: 'Hide', settings_sidebar_width: 'Sidebar Width (px)', settings_width_range_hint: '(Range: 90-270, Step: 5)', settings_font_size: 'Base Font Size (px)', settings_font_size_range_hint: '(Range: 8-24, Step: 0.5)', settings_header_icon_size: 'Header Icon Size (px)', settings_header_icon_size_range_hint: '(Range: 8-32, Step: 0.5)', settings_vertical_spacing: 'Vertical Spacing', settings_vertical_spacing_range_hint: '(Multiplier Range: 0.05-1.5, Step: 0.05)', settings_theme: 'Theme:', settings_theme_system: 'Follow System', settings_theme_light: 'Light', settings_theme_dark: 'Dark', settings_theme_minimal_light: 'Minimal (Light)', settings_theme_minimal_dark: 'Minimal (Dark)', settings_hover_mode: 'Hover Mode', settings_idle_opacity: 'Idle Opacity:', settings_opacity_range_hint: '(Range: 0.1-1.0, Step: 0.05)', settings_country_display: 'Country/Region Display:', settings_country_display_icontext: 'Icon & Text', settings_country_display_text: 'Text Only', settings_country_display_icon: 'Icon Only', settings_visible_sections: 'Visible Sections:', settings_section_order: 'Adjust Sidebar Section Order:', settings_section_order_hint: '(Only affects checked sections)', settings_no_orderable_sections: 'No visible sections to order.', settings_move_up_title: 'Move Up', settings_move_down_title: 'Move Down', settings_custom_intro: 'Manage custom filter options for each section:', settings_manage_sites_button: 'Manage Favorite Sites...', settings_manage_languages_button: 'Manage Custom Languages...', settings_manage_countries_button: 'Manage Countries/Regions...', settings_manage_time_ranges_button: 'Manage Time Ranges...', settings_manage_file_types_button: 'Manage File Types...', settings_save_button: 'Save Settings', settings_cancel_button: 'Cancel', settings_reset_all_button: 'Reset All',
                modal_label_enable_predefined: 'Enable Predefined {type}:', modal_label_my_custom: 'My Custom {type}:', modal_placeholder_name: 'Name', modal_placeholder_domain: 'Domain', modal_placeholder_text: 'Text', modal_placeholder_value: 'Value', modal_hint_domain: 'Format: valid top-level domain, e.g., `wikipedia.org`', modal_hint_language: 'Format: starts with `lang_`, e.g., `lang_ja`, `lang_zh-TW`. Use `|` for multiple.', modal_hint_country: 'Format: `country` + 2-letter uppercase code, e.g., `countryDE`', modal_hint_time: 'Format: `h`, `d`, `w`, `m`, `y`, optionally followed by numbers, e.g., `h1`, `d7`, `w`', modal_hint_filetype: 'Format: file extension, e.g., `pdf`, `docx`', modal_tooltip_domain: 'Please enter a valid domain', modal_tooltip_language: 'Format: lang_xx or lang_xx-XX, separate multiple with |', modal_tooltip_country: 'Format: countryXX (XX = uppercase country code)', modal_tooltip_time: 'Format: h, d, w, m, y, optionally followed by numbers', modal_tooltip_filetype: 'File extension (without the dot)', modal_button_add_title: 'Add', modal_button_update_title: 'Update Item', modal_button_cancel_edit_title: 'Cancel Edit', modal_button_edit_title: 'Edit', modal_button_delete_title: 'Delete', modal_button_complete: 'Done', value_empty: '(empty)', date_range_from: 'From:', date_range_to: 'To:', sidebar_collapse_title: 'Collapse', sidebar_expand_title: 'Expand', sidebar_drag_title: 'Drag', sidebar_settings_title: 'Settings',
                alert_invalid_start_date: 'Invalid start date', alert_invalid_end_date: 'Invalid end date', alert_end_before_start: 'End date cannot be earlier than start date', alert_start_in_future: 'Start date cannot be in the future', alert_end_in_future: 'End date cannot be in the future', alert_select_date: 'Please select a date', alert_error_applying_date: 'Error applying date range', alert_error_applying_filter: 'Error applying filter {type}={value}', alert_error_applying_site_search: 'Error applying site search for {site}', alert_error_clearing_site_search: 'Error clearing site search', alert_error_resetting_filters: 'Error resetting filters', alert_error_toggling_verbatim: 'Error toggling Verbatim search', alert_error_toggling_personalization: 'Error toggling Personalization search', alert_enter_display_name: 'Please enter the display name for {type}.', alert_enter_value: 'Please enter the corresponding value for {type}.', alert_invalid_value_format: 'The value format for {type} is incorrect. {hint}', alert_duplicate_name: 'Custom item display name "{name}" already exists. Please use a different name.', alert_update_failed_invalid_index: 'Update failed: Invalid item index.', alert_edit_failed_missing_fields: 'Cannot edit: Input or button fields not found.', confirm_delete_item: 'Are you sure you want to delete the item "{name}"?', confirm_reset_settings: 'Are you sure you want to reset all settings to their default values?', alert_settings_reset_success: 'Settings have been reset to default. You can continue editing or click "Save Settings" to confirm.', confirm_reset_all_menu: 'Are you sure you want to reset all settings to their default values?\nThis cannot be undone and requires a page refresh to take effect.', alert_reset_all_menu_success: 'All settings have been reset to defaults.\nPlease refresh the page to apply the changes.', alert_reset_all_menu_fail: 'Failed to reset settings via menu command! Please check the console.', alert_init_fail: '{scriptName} initialization failed. Some features may not work. Please check the console for technical details.\nTechnical Error: {error}', menu_open_settings: '⚙️ Open Settings', menu_reset_all_settings: '🚨 Reset All Settings',
            }
        };
        let effectiveTranslations = JSON.parse(JSON.stringify(builtInTranslations));
        let _currentLocale = 'en';
        function _mergeExternalTranslations() { if (typeof window.GSCS_Namespace !== 'undefined' && typeof window.GSCS_Namespace.i18nPack === 'object' && typeof window.GSCS_Namespace.i18nPack.translations === 'object') { const externalTranslations = window.GSCS_Namespace.i18nPack.translations; for (const langCode in externalTranslations) { if (Object.prototype.hasOwnProperty.call(externalTranslations, langCode)) { if (!effectiveTranslations[langCode]) { effectiveTranslations[langCode] = {}; } Object.assign(effectiveTranslations[langCode], externalTranslations[langCode]); } } } else { console.warn(`${LOG_PREFIX} [i18n] External i18n pack (window.GSCS_Namespace.i18nPack) not found or invalid. Using built-in translations only.`); } }
        function _detectBrowserLocale() { let locale = 'en'; try { if (navigator.languages && navigator.languages.length) { locale = navigator.languages[0]; } else if (navigator.language) { locale = navigator.language; } } catch (e) { console.warn(`${LOG_PREFIX} [i18n] Error accessing navigator.language(s):`, e); } if (effectiveTranslations[locale]) return locale; if (locale.includes('-')) { const parts = locale.split('-'); if (parts.length > 0 && effectiveTranslations[parts[0]]) return parts[0]; if (parts.length > 2 && effectiveTranslations[`${parts[0]}-${parts[1]}`]) return `${parts[0]}-${parts[1]}`; } return 'en'; }
        function _updateActiveLocale(settingsToUse) { let newLocale = 'en'; const langSettingSource = (settingsToUse && Object.keys(settingsToUse).length > 0 && typeof settingsToUse.interfaceLanguage === 'string') ? settingsToUse : defaultSettings; const userSelectedLang = langSettingSource.interfaceLanguage; if (userSelectedLang && userSelectedLang !== 'auto') { if (effectiveTranslations[userSelectedLang]) { newLocale = userSelectedLang; } else if (userSelectedLang.includes('-')) { const genericLang = userSelectedLang.split('-')[0]; if (effectiveTranslations[genericLang]) { newLocale = genericLang; } else { newLocale = _detectBrowserLocale(); } } else { newLocale = _detectBrowserLocale(); } } else { newLocale = _detectBrowserLocale(); } if (_currentLocale !== newLocale) { _currentLocale = newLocale; } if (userSelectedLang && userSelectedLang !== 'auto' && _currentLocale !== userSelectedLang && !userSelectedLang.includes(_currentLocale)) { console.warn(`${LOG_PREFIX} [i18n] User selected language "${userSelectedLang}" was not fully available or matched. Using best match: "${_currentLocale}".`); } }
        _mergeExternalTranslations();
        function getString(key, replacements = {}) { let str = `[ERR: ${key} @ ${_currentLocale}]`; let found = false; if (effectiveTranslations[_currentLocale] && typeof effectiveTranslations[_currentLocale][key] !== 'undefined') { str = effectiveTranslations[_currentLocale][key]; found = true; } else if (_currentLocale.includes('-')) { const genericLang = _currentLocale.split('-')[0]; if (effectiveTranslations[genericLang] && typeof effectiveTranslations[genericLang][key] !== 'undefined') { str = effectiveTranslations[genericLang][key]; found = true; } } if (!found && _currentLocale !== 'en') { if (effectiveTranslations['en'] && typeof effectiveTranslations['en'][key] !== 'undefined') { str = effectiveTranslations['en'][key]; found = true; } } if (!found) { if (!(effectiveTranslations['en'] && typeof effectiveTranslations['en'][key] !== 'undefined')) { console.error(`${LOG_PREFIX} [i18n] CRITICAL: Missing translation for key: "${key}" in BOTH locale: "${_currentLocale}" AND default locale "en".`); } else { str = effectiveTranslations['en'][key]; found = true; } if(!found) str = `[ERR_NF: ${key}]`; } if (typeof str === 'string') { for (const placeholder in replacements) { if (Object.prototype.hasOwnProperty.call(replacements, placeholder)) { str = str.replace(new RegExp(`\\{${placeholder}\\}`, 'g'), replacements[placeholder]); } } } else { console.error(`${LOG_PREFIX} [i18n] CRITICAL: Translation for key "${key}" is not a string:`, str); return `[INVALID_TYPE_FOR_KEY: ${key}]`; } return str; }
        return { getString: getString, getCurrentLocale: function() { return _currentLocale; }, getTranslationsForLocale: function(locale = 'en') { return effectiveTranslations[locale] || effectiveTranslations['en']; }, initializeBaseLocale: function() { _updateActiveLocale(defaultSettings); }, updateActiveLocale: function(activeSettings) { _updateActiveLocale(activeSettings); }, getAvailableLocales: function() { const locales = new Set(['auto', 'en']); Object.keys(effectiveTranslations).forEach(lang => { if (Object.keys(effectiveTranslations[lang]).length > 0) { locales.add(lang); } }); return Array.from(locales).sort((a, b) => { if (a === 'auto') return -1; if (b === 'auto') return 1; if (a === 'en' && b !== 'auto') return -1; if (b === 'en' && a !== 'auto') return 1; let nameA = a, nameB = b; try { nameA = new Intl.DisplayNames([a],{type:'language'}).of(a); } catch(e){} try { nameB = new Intl.DisplayNames([b],{type:'language'}).of(b); } catch(e){} return nameA.localeCompare(nameB); }); } };
    })();
    const _ = LocalizationService.getString;
    const Utils = { /* ... (same as before) ... */ debounce: function(func, wait) { let timeout; return function executedFunction(...args) { const context = this; const later = () => { timeout = null; func.apply(context, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, mergeDeep: function(target, source) { if (!source) return target; target = target || {}; for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { const targetValue = target[key]; const sourceValue = source[key]; if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue)) { target[key] = Utils.mergeDeep(targetValue, sourceValue); } else if (typeof sourceValue !== 'undefined') { target[key] = sourceValue; } } } return target; }, clamp: function(num, min, max) { return Math.min(Math.max(num, min), max); }, parseIconAndText: function(fullText) { const match = fullText.match(/^(\P{L}\P{N}\s*)+/u); let icon = ''; let text = fullText; if (match && match[0].trim() !== '') { icon = match[0].trim(); text = fullText.substring(icon.length).trim(); } return { icon, text }; }, getCurrentURL: function() { try { return new URL(window.location.href); } catch (e) { console.error(`${LOG_PREFIX} Error creating URL object:`, e); return null; } } };
    const NotificationManager = (function() { /* ... (same as before) ... */ let container = null; function init() { if (document.getElementById(IDS.NOTIFICATION_CONTAINER)) { container = document.getElementById(IDS.NOTIFICATION_CONTAINER); return; } container = document.createElement('div'); container.id = IDS.NOTIFICATION_CONTAINER; if (document.body) { document.body.appendChild(container); } else { console.error(LOG_PREFIX + " NotificationManager.init(): document.body is not available!"); container = null; } } function show(messageKey, messageArgs = {}, type = 'info', duration = 3000) { if (!container) { const alertMsg = (typeof _ === 'function' && _(messageKey, messageArgs) && !(_(messageKey, messageArgs).startsWith('[ERR:'))) ? _(messageKey, messageArgs) : `${messageKey} (args: ${JSON.stringify(messageArgs)})`; alert(alertMsg); return null; } const notificationElement = document.createElement('div'); notificationElement.classList.add(CSS.NOTIFICATION); const typeClass = CSS[`NTF_${type.toUpperCase()}`] || CSS.NTF_INFO; notificationElement.classList.add(typeClass); notificationElement.textContent = _(messageKey, messageArgs); if (duration <= 0) { const closeButton = document.createElement('span'); closeButton.innerHTML = '&times;'; closeButton.style.cursor = 'pointer'; closeButton.style.marginLeft = '10px'; closeButton.style.float = 'right'; closeButton.onclick = () => notificationElement.remove(); notificationElement.appendChild(closeButton); } container.appendChild(notificationElement); if (duration > 0) { setTimeout(() => { notificationElement.style.opacity = '0'; setTimeout(() => notificationElement.remove(), 500); }, duration); } return notificationElement; } return { init: init, show: show }; })();
    function getListMapping(listId) { /* ... (same as before) ... */ const listMappings = { [IDS.SITES_LIST]: { arrayKey: 'favoriteSites', valueKey: 'url', populateFn: populateFavoriteSitesList, textInput: `#${IDS.NEW_SITE_NAME}`, valueInput: `#${IDS.NEW_SITE_URL}`, addButton: `#${IDS.ADD_SITE_BTN}`, nameKey: 'section_site_search' }, [IDS.LANG_LIST]: { arrayKey: 'customLanguages', valueKey: 'value', populateFn: populateCustomLanguagesList, textInput: `#${IDS.NEW_LANG_TEXT}`, valueInput: `#${IDS.NEW_LANG_VALUE}`, addButton: `#${IDS.ADD_LANG_BTN}`, nameKey: 'section_language' }, [IDS.TIME_LIST]: { arrayKey: 'customTimeRanges', valueKey: 'value', populateFn: populateCustomTimeRangesList,textInput: `#${IDS.NEW_TIME_TEXT}`, valueInput: `#${IDS.NEW_TIME_VALUE}`, addButton: `#${IDS.ADD_TIME_BTN}`, nameKey: 'section_time' }, [IDS.FT_LIST]: { arrayKey: 'customFiletypes', valueKey: 'value', populateFn: populateCustomFiletypesList, textInput: `#${IDS.NEW_FT_TEXT}`, valueInput: `#${IDS.NEW_FT_VALUE}`, addButton: `#${IDS.ADD_FT_BTN}`, nameKey: 'section_filetype' }, [IDS.COUNTRIES_LIST]: { arrayKey: 'customCountries', valueKey: 'value', populateFn: populateCustomCountriesList, textInput: `#${IDS.NEW_COUNTRY_TEXT}`, valueInput: `#${IDS.NEW_COUNTRY_VALUE}`, addButton: `#${IDS.ADD_COUNTRY_BTN}`, nameKey: 'section_country' } }; return listMappings[listId] || null; }
    function validateCustomInput(inputElement) { /* ... (same as before) ... */ if (!inputElement) return false; const value = inputElement.value.trim(); const id = inputElement.id; let isValid = false; let isEmpty = value === ''; if (id === IDS.NEW_SITE_NAME || id === IDS.NEW_LANG_TEXT || id === IDS.NEW_TIME_TEXT || id === IDS.NEW_FT_TEXT || id === IDS.NEW_COUNTRY_TEXT) { isValid = !isEmpty; } else if (id === IDS.NEW_SITE_URL) { isValid = isEmpty || /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(value); } else if (id === IDS.NEW_LANG_VALUE) { isValid = isEmpty || /^lang_[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,4})?(?:\|lang_[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,4})?)*$/.test(value); } else if (id === IDS.NEW_TIME_VALUE) { isValid = isEmpty || /^[hdwmy]\d*$/.test(value); } else if (id === IDS.NEW_FT_VALUE) { isValid = isEmpty || /^[a-zA-Z0-9]+$/.test(value); } else if (id === IDS.NEW_COUNTRY_VALUE) { isValid = isEmpty || /^country[A-Z]{2}$/.test(value); } inputElement.classList.remove('input-valid', 'input-invalid', CSS.INPUT_HAS_ERROR); _clearInputError(inputElement); if (!isEmpty) { inputElement.classList.add(isValid ? 'input-valid' : 'input-invalid'); if (!isValid) inputElement.classList.add(CSS.INPUT_HAS_ERROR); } return isValid || isEmpty; }
    function _getInputErrorElement(inputElement) { /* ... (same as before) ... */ if (!inputElement || !inputElement.id) return null; let errorEl = inputElement.nextElementSibling; if (errorEl && errorEl.classList.contains(CSS.INPUT_ERROR_MESSAGE) && errorEl.id === `${inputElement.id}-error-msg`) { return errorEl; } const parentDiv = inputElement.parentElement; if (parentDiv) { return parentDiv.querySelector(`#${inputElement.id}-error-msg`); } return null; }
    function _showInputError(inputElement, messageKey, messageArgs = {}) { /* ... (same as before) ... */ if (!inputElement) return; const errorElement = _getInputErrorElement(inputElement); if (errorElement) { errorElement.textContent = _(messageKey, messageArgs); errorElement.classList.add(CSS.ERROR_VISIBLE); } inputElement.classList.add(CSS.INPUT_HAS_ERROR); inputElement.classList.remove('input-valid'); }
    function _clearInputError(inputElement) { /* ... (same as before) ... */ if (!inputElement) return; const errorElement = _getInputErrorElement(inputElement); if (errorElement) { errorElement.textContent = ''; errorElement.classList.remove(CSS.ERROR_VISIBLE); } inputElement.classList.remove(CSS.INPUT_HAS_ERROR, 'input-invalid'); }
    function _clearAllInputErrorsInGroup(inputGroupElement) { /* ... (same as before) ... */ if (!inputGroupElement) return; inputGroupElement.querySelectorAll(`input[type="text"]`).forEach(input => { _clearInputError(input); input.classList.remove('input-valid', 'input-invalid'); }); }
    function _showGlobalMessage(messageKey, messageArgs = {}, type = 'info', duration = 3000, targetElementId = IDS.SETTINGS_MESSAGE_BAR) { /* ... (same as before) ... */ const messageBar = document.getElementById(targetElementId); if (!messageBar) { if (targetElementId !== IDS.SETTINGS_MESSAGE_BAR && NotificationManager && typeof NotificationManager.show === 'function') { NotificationManager.show(messageKey, messageArgs, type, duration > 0 ? duration : 5000); } else { const alertMsg = (typeof _ === 'function' && _(messageKey, messageArgs) && !(_(messageKey, messageArgs).startsWith('[ERR:'))) ? _(messageKey, messageArgs) : `${messageKey} (args: ${JSON.stringify(messageArgs)})`; alert(alertMsg); } return; } if (globalMessageTimeout && targetElementId === IDS.SETTINGS_MESSAGE_BAR) { clearTimeout(globalMessageTimeout); globalMessageTimeout = null; } messageBar.textContent = _(messageKey, messageArgs); messageBar.className = `${CSS.MESSAGE_BAR}`; messageBar.classList.add(CSS[`MSG_${type.toUpperCase()}`] || CSS.MSG_INFO); messageBar.style.display = 'block'; if (duration > 0 && targetElementId === IDS.SETTINGS_MESSAGE_BAR) { globalMessageTimeout = setTimeout(() => { messageBar.style.display = 'none'; messageBar.textContent = ''; messageBar.className = CSS.MESSAGE_BAR; }, duration); } }
    function _validateAndPrepareCustomItemData(textInput, valueInput, itemTypeName, listId) { /* ... (same as before) ... */ if (!textInput || !valueInput) { _showGlobalMessage('alert_edit_failed_missing_fields', {}, 'error', 0); return { isValid: false }; } _clearInputError(textInput); _clearInputError(valueInput); const text = textInput.value.trim(); const value = valueInput.value.trim(); let hint = ''; if (text === '') { _showInputError(textInput, 'alert_enter_display_name', { type: itemTypeName }); textInput.focus(); return { isValid: false, errorField: textInput }; } else { textInput.classList.remove(CSS.INPUT_HAS_ERROR); validateCustomInput(textInput); } if (value === '') { _showInputError(valueInput, 'alert_enter_value', { type: itemTypeName }); valueInput.focus(); return { isValid: false, errorField: valueInput }; } else { const isValueFormatValid = validateCustomInput(valueInput); if (!isValueFormatValid && !valueInput.classList.contains('input-invalid')) { if (listId === IDS.COUNTRIES_LIST) hint = _('modal_tooltip_country'); else if (listId === IDS.LANG_LIST) hint = _('modal_tooltip_language'); else if (listId === IDS.TIME_LIST) hint = _('modal_tooltip_time'); else if (listId === IDS.FT_LIST) hint = _('modal_tooltip_filetype'); else if (listId === IDS.SITES_LIST) hint = _('modal_tooltip_domain'); _showInputError(valueInput, 'alert_invalid_value_format', { type: itemTypeName, hint: hint }); valueInput.focus(); return { isValid: false, errorField: valueInput }; } else if (!isValueFormatValid) { valueInput.focus(); return { isValid: false, errorField: valueInput }; } else { valueInput.classList.remove(CSS.INPUT_HAS_ERROR); } } textInput.classList.remove(CSS.INPUT_HAS_ERROR); valueInput.classList.remove(CSS.INPUT_HAS_ERROR); return { isValid: true, text: text, value: value }; }
    function _isDuplicateCustomItem(text, itemsToCheck, listId, editingIndex, editingItemInfoRef) { /* ... (same as before) ... */ const lowerText = text.toLowerCase(); return itemsToCheck.some((item, idx) => { if (editingItemInfoRef && editingItemInfoRef.listId === listId && editingIndex === idx) { return false; } return item.text.toLowerCase() === lowerText; }); }
    function createCustomListItem(index, item, valueKey, listId) { /* ... (same as before) ... */ const listItem = document.createElement('li'); listItem.dataset[DATA_ATTR.INDEX] = index; listItem.dataset[DATA_ATTR.LIST_ID] = listId; const textSpan = document.createElement('span'); let displayValue = item[valueKey] ?? _('value_empty'); let displayKey = valueKey; if (listId === IDS.LANG_LIST) displayKey = 'lr'; else if (listId === IDS.FT_LIST) displayKey = 'filetype'; else if (listId === IDS.SITES_LIST) displayKey = 'site'; else if (listId === IDS.TIME_LIST) displayKey = 'qdr'; else if (listId === IDS.COUNTRIES_LIST) displayKey = 'cr'; textSpan.textContent = `${item.text} (${displayKey}=${displayValue})`; textSpan.title = textSpan.textContent; const controlsSpan = document.createElement('span'); controlsSpan.classList.add(CSS.ITEM_CONTROLS); controlsSpan.innerHTML = `<button class="${CSS.EDIT_CUSTOM_ITEM}" title="${_('modal_button_edit_title')}">${SVG_ICONS.edit}</button> <button class="${CSS.DELETE_CUSTOM_ITEM}" title="${_('modal_button_delete_title')}">${SVG_ICONS.delete}</button>`; listItem.appendChild(textSpan); listItem.appendChild(controlsSpan); return listItem; }
    function populateCustomList(listElementId, items, valueKey, contextElement = document) { /* ... (same as before) ... */ const listElement = contextElement.querySelector(`#${listElementId}`); if (!listElement) { console.warn(`${LOG_PREFIX} List element not found: #${listElementId}`); return; } listElement.innerHTML = ''; const fragment = document.createDocumentFragment(); if (!Array.isArray(items)) items = []; items.forEach((item, index) => { if (item && typeof item.text === 'string' && typeof item[valueKey] === 'string') { fragment.appendChild(createCustomListItem(index, item, valueKey, listElementId)); } }); listElement.appendChild(fragment); }
    function populateFavoriteSitesList(sites, context = document) { /* ... (same as before) ... */ populateCustomList(IDS.SITES_LIST, sites, 'url', context); }
    function populateCustomLanguagesList(langs, context = document) { /* ... (same as before) ... */ populateCustomList(IDS.LANG_LIST, langs, 'value', context); }
    function populateCustomTimeRangesList(ranges, context = document) { /* ... (same as before) ... */ populateCustomList(IDS.TIME_LIST, ranges, 'value', context); }
    function populateCustomFiletypesList(types, context = document) { /* ... (same as before) ... */ populateCustomList(IDS.FT_LIST, types, 'value', context); }
    function populateCustomCountriesList(countries, context = document) { /* ... (same as before) ... */ populateCustomList(IDS.COUNTRIES_LIST, countries, 'value', context); }
    function applyThemeToElement(element, themeSetting) { /* ... (same as before) ... */ if (!element) return; element.classList.remove( CSS.LIGHT_THEME, CSS.DARK_THEME, 'minimal-theme', 'minimal-light', 'minimal-dark' ); let effectiveTheme = themeSetting; const isSettingsOrModal = element.id === IDS.SETTINGS_WINDOW || element.id === IDS.SETTINGS_OVERLAY || element.classList.contains('settings-modal-content') || element.classList.contains('settings-modal-overlay'); if (isSettingsOrModal) { if (themeSetting === 'minimal-light') effectiveTheme = 'light'; else if (themeSetting === 'minimal-dark') effectiveTheme = 'dark'; } switch (effectiveTheme) { case 'dark': element.classList.add(CSS.DARK_THEME); break; case 'minimal-light': element.classList.add('minimal-theme', 'minimal-light'); break; case 'minimal-dark':  element.classList.add('minimal-theme', 'minimal-dark'); break; case 'system': const systemIsDark = systemThemeMediaQuery && systemThemeMediaQuery.matches; element.classList.add(systemIsDark ? CSS.DARK_THEME : CSS.LIGHT_THEME); break; case 'light': default: element.classList.add(CSS.LIGHT_THEME); break; } }
    const ModalManager = (function() { /* ... (same as before, ModalManager remains unchanged internally) ... */ let _currentModal = null; let _currentModalContent = null; let _editingItemInfo = null; function _createPredefinedListHTML(currentOptionType, typeNameKey, predefinedOptionsSource, enabledPredefinedValues) { const label = _(typeNameKey); const optionsHTML = (predefinedOptionsSource[currentOptionType] || []).map(option => { const checkboxId = `predefined-${currentOptionType}-${option.value.replace(/[^a-zA-Z0-9-_]/g, '')}`; const translatedOptionText = _(option.textKey); const isChecked = enabledPredefinedValues.has(option.value); return `<li><input type="checkbox" id="${checkboxId}" value="${option.value}" data-option-type="${currentOptionType}" ${isChecked ? 'checked' : ''}><label for="${checkboxId}">${translatedOptionText}</label></li>`; }).join(''); return `<label style="font-weight: bold;">${_('modal_label_enable_predefined', { type: label })}</label><ul class="predefined-options-list" data-option-type="${currentOptionType}">${optionsHTML}</ul>`; } function _createCustomListHTML(currentListId, textPlaceholderKey, valuePlaceholderKey, hintKey, formatTooltipKey) { const mapping = getListMapping(currentListId); if (!mapping) return ''; const typeName = _(mapping.nameKey); const textInputId = mapping.textInput.substring(1); const valueInputId = mapping.valueInput.substring(1); const inputGroupHTML = `<div class="${CSS.CUSTOM_LIST_INPUT_GROUP}"><div><input type="text" id="${textInputId}" placeholder="${_(textPlaceholderKey)}"><span id="${textInputId}-error-msg" class="${CSS.INPUT_ERROR_MESSAGE}"></span></div><div><input type="text" id="${valueInputId}" placeholder="${_(valuePlaceholderKey)}" title="${_(formatTooltipKey)}"><span id="${valueInputId}-error-msg" class="${CSS.INPUT_ERROR_MESSAGE}"></span></div><button id="${mapping.addButton.substring(1)}" class="${CSS.ADD_CUSTOM_BUTTON} custom-list-action-button" data-list-id="${currentListId}" title="${_('modal_button_add_title')}">${SVG_ICONS.add}</button><button class="cancel-edit-button" style="display: none;" title="${_('modal_button_cancel_edit_title')}">${SVG_ICONS.close}</button></div>`; const hintHTML = `<span class="setting-value-hint">${_(hintKey)}</span>`; return `<label style="font-weight: bold; margin-top: 1em; display: block;">${_('modal_label_my_custom', { type: typeName })}</label><ul id="${currentListId}" class="${CSS.CUSTOM_LIST}"></ul>${inputGroupHTML}${hintHTML}`; } function _resetEditStateInternal(contextElement = _currentModalContent) { if (_editingItemInfo) { const mapping = getListMapping(_editingItemInfo.listId); if (_editingItemInfo.addButton) { _editingItemInfo.addButton.innerHTML = SVG_ICONS.add; _editingItemInfo.addButton.title = _('modal_button_add_title'); _editingItemInfo.addButton.classList.remove('update-mode'); } if (_editingItemInfo.cancelButton) { _editingItemInfo.cancelButton.style.display = 'none'; } if (mapping && contextElement) { const textInput = contextElement.querySelector(mapping.textInput); const valueInput = contextElement.querySelector(mapping.valueInput); const inputGroup = textInput?.closest(`.${CSS.CUSTOM_LIST_INPUT_GROUP}`); if(inputGroup) _clearAllInputErrorsInGroup(inputGroup); if (textInput) { textInput.value = ''; textInput.classList.remove('input-valid', 'input-invalid', CSS.INPUT_HAS_ERROR); _clearInputError(textInput);} if (valueInput) { valueInput.value = ''; valueInput.classList.remove('input-valid', 'input-invalid', CSS.INPUT_HAS_ERROR); _clearInputError(valueInput);} } } _editingItemInfo = null; } function _prepareEditItemActionInternal(item, index, listId, mapping, contextElement) { const textInput = contextElement.querySelector(mapping.textInput); const valueInput = contextElement.querySelector(mapping.valueInput); const addButton = contextElement.querySelector(mapping.addButton); const cancelButton = addButton?.parentElement?.querySelector('.cancel-edit-button'); if (textInput && valueInput && addButton && cancelButton) { if (_editingItemInfo && (_editingItemInfo.listId !== listId || _editingItemInfo.index !== index)) { _resetEditStateInternal(contextElement); } textInput.value = item.text; valueInput.value = item[mapping.valueKey]; _editingItemInfo = { listId, index, addButton, cancelButton, arrayKey: mapping.arrayKey }; addButton.innerHTML = SVG_ICONS.update; addButton.title = _('modal_button_update_title'); addButton.classList.add('update-mode'); cancelButton.style.display = 'inline-block'; validateCustomInput(valueInput); validateCustomInput(textInput); textInput.focus(); } else { const errorSourceInput = textInput || valueInput || addButton?.closest(`.${CSS.CUSTOM_LIST_INPUT_GROUP}`)?.querySelector('input[type="text"]'); if (errorSourceInput) _showInputError(errorSourceInput, 'alert_edit_failed_missing_fields'); else _showGlobalMessage('alert_edit_failed_missing_fields', {}, 'error', 0, _currentModalContent?.querySelector(`#${IDS.SETTINGS_MESSAGE_BAR}`) ? IDS.SETTINGS_MESSAGE_BAR : null); } } function _handleCustomListActionsInternal(event, contextElement, tempCustomItemsRef) { const button = event.target.closest(`button.${CSS.EDIT_CUSTOM_ITEM}, button.${CSS.DELETE_CUSTOM_ITEM}`); if (!button) return; const listItem = button.closest(`li[data-${DATA_ATTR.INDEX}][data-list-id]`); if (!listItem) return; const index = parseInt(listItem.dataset[DATA_ATTR.INDEX], 10); const listId = listItem.getAttribute('data-list-id'); if (isNaN(index) || !listId) return; const mapping = getListMapping(listId); if (!mapping) return; const targetArray = tempCustomItemsRef; if (!Array.isArray(targetArray) || index < 0 || index >= targetArray.length) return; const item = targetArray[index]; if (button.classList.contains(CSS.DELETE_CUSTOM_ITEM)) { if (confirm(_('confirm_delete_item', { name: item.text }))) { if (_editingItemInfo && _editingItemInfo.listId === listId && _editingItemInfo.index === index) { _resetEditStateInternal(contextElement); } targetArray.splice(index, 1); mapping.populateFn(targetArray, contextElement); } } else if (button.classList.contains(CSS.EDIT_CUSTOM_ITEM)) { _prepareEditItemActionInternal(item, index, listId, mapping, contextElement); } } function _handleCustomItemSubmitInternal(listId, contextElement, tempCustomItemsRef) { const mapping = getListMapping(listId); if (!mapping) return; const { arrayKey, valueKey, populateFn, textInput: textInputSelector, valueInput: valueInputSelector, nameKey } = mapping; const itemTypeName = _(nameKey); const textInput = contextElement.querySelector(textInputSelector); const valueInput = contextElement.querySelector(valueInputSelector); const inputGroup = textInput?.closest(`.${CSS.CUSTOM_LIST_INPUT_GROUP}`); if (inputGroup) _clearAllInputErrorsInGroup(inputGroup); const validationResult = _validateAndPrepareCustomItemData(textInput, valueInput, itemTypeName, listId); if (!validationResult.isValid) { if (validationResult.errorField) validationResult.errorField.focus(); return; } const { text, value } = validationResult; const itemsToCheck = tempCustomItemsRef; const editingIdx = (_editingItemInfo && _editingItemInfo.listId === listId) ? _editingItemInfo.index : -1; if (_isDuplicateCustomItem(text, itemsToCheck, listId, editingIdx, _editingItemInfo)) { if (textInput) _showInputError(textInput, 'alert_duplicate_name', { name: text }); textInput?.focus(); return; } const itemData = { text: text, value: value }; if (_editingItemInfo && _editingItemInfo.listId === listId) { const indexToUpdate = _editingItemInfo.index; if (indexToUpdate >= 0 && indexToUpdate < itemsToCheck.length) { itemsToCheck[indexToUpdate] = { text: itemData.text, [valueKey]: itemData.value }; populateFn(itemsToCheck, contextElement); _resetEditStateInternal(contextElement); } else { if(textInput) _showInputError(textInput, 'alert_update_failed_invalid_index'); _resetEditStateInternal(contextElement); } } else { itemsToCheck.push({ text: itemData.text, [valueKey]: itemData.value }); populateFn(itemsToCheck, contextElement); if (textInput) { textInput.value = ''; _clearInputError(textInput); textInput.focus(); } if (valueInput) { valueInput.value = ''; _clearInputError(valueInput); } } } function _bindModalContentEventsInternal(modalContent, tempCustomItemsArrayRef) { if (!modalContent || modalContent.dataset.modalEventsBound === 'true') return; modalContent.dataset.modalEventsBound = 'true'; modalContent.addEventListener('click', (event) => { const target = event.target; let listIdForAction = ''; const addButton = target.closest(`.${CSS.ADD_CUSTOM_BUTTON}.custom-list-action-button`); const itemControlButton = target.closest(`.${CSS.EDIT_CUSTOM_ITEM}, .${CSS.DELETE_CUSTOM_ITEM}`); const cancelEditButton = target.closest('.cancel-edit-button'); if (addButton) listIdForAction = addButton.dataset.listId; else if (itemControlButton) listIdForAction = itemControlButton.closest('li')?.dataset.listId; else if (cancelEditButton) { const inputGroup = cancelEditButton.closest(`.${CSS.CUSTOM_LIST_INPUT_GROUP}`); listIdForAction = inputGroup?.querySelector(`.${CSS.ADD_CUSTOM_BUTTON}`)?.dataset.listId; if (inputGroup) _clearAllInputErrorsInGroup(inputGroup); } if (itemControlButton && listIdForAction) { _handleCustomListActionsInternal(event, modalContent, tempCustomItemsArrayRef); return; } if (addButton && listIdForAction) { _handleCustomItemSubmitInternal(listIdForAction, modalContent, tempCustomItemsArrayRef); return; } if (cancelEditButton) { _resetEditStateInternal(modalContent); return; } }); modalContent.addEventListener('input', (event) => { const target = event.target; if (target.matches(`#${IDS.NEW_SITE_NAME}, #${IDS.NEW_SITE_URL}, #${IDS.NEW_LANG_TEXT}, #${IDS.NEW_LANG_VALUE}, #${IDS.NEW_TIME_TEXT}, #${IDS.NEW_TIME_VALUE}, #${IDS.NEW_FT_TEXT}, #${IDS.NEW_FT_VALUE}, #${IDS.NEW_COUNTRY_TEXT}, #${IDS.NEW_COUNTRY_VALUE}`)) { _clearInputError(target); validateCustomInput(target); } }); } return { show: function(titleKey, contentHTML, onCompleteCallback, currentTheme) { this.hide(); _currentModal = document.createElement('div'); _currentModal.className = 'settings-modal-overlay'; applyThemeToElement(_currentModal, currentTheme); _currentModalContent = document.createElement('div'); _currentModalContent.className = 'settings-modal-content'; applyThemeToElement(_currentModalContent, currentTheme); const header = document.createElement('div'); header.className = 'settings-modal-header'; header.innerHTML = `<h4>${_(titleKey)}</h4><button class="settings-modal-close-btn" title="${_('settings_close_button_title')}">${SVG_ICONS.close}</button>`; const body = document.createElement('div'); body.className = 'settings-modal-body'; body.innerHTML = contentHTML; const footer = document.createElement('div'); footer.className = 'settings-modal-footer'; footer.innerHTML = `<button class="modal-complete-btn">${_('modal_button_complete')}</button>`; _currentModalContent.appendChild(header); _currentModalContent.appendChild(body); _currentModalContent.appendChild(footer); _currentModal.appendChild(_currentModalContent); let closeModalHandlerInstance = null; let completeModalHandlerInstance = null; const self = this; closeModalHandlerInstance = (event) => { if (event.target === _currentModal || event.target.closest('.settings-modal-close-btn')) { self.hide(true); _currentModal?.removeEventListener('click', closeModalHandlerInstance, true); header.querySelector('.settings-modal-close-btn')?.removeEventListener('click', closeModalHandlerInstance); footer.querySelector('.modal-complete-btn')?.removeEventListener('click', completeModalHandlerInstance); } }; completeModalHandlerInstance = () => { if (onCompleteCallback && typeof onCompleteCallback === 'function') { onCompleteCallback(_currentModalContent); } _currentModal?.removeEventListener('click', closeModalHandlerInstance, true); header.querySelector('.settings-modal-close-btn')?.removeEventListener('click', closeModalHandlerInstance); footer.querySelector('.modal-complete-btn')?.removeEventListener('click', completeModalHandlerInstance); self.hide(false); }; _currentModal.addEventListener('click', closeModalHandlerInstance, true); header.querySelector('.settings-modal-close-btn').addEventListener('click', closeModalHandlerInstance); footer.querySelector('.modal-complete-btn').addEventListener('click', completeModalHandlerInstance); _currentModalContent.addEventListener('click', (event) => event.stopPropagation()); document.body.appendChild(_currentModal); return _currentModalContent; }, hide: function(isCancel = false) { if (_currentModal) { const inputGroup = _currentModalContent?.querySelector(`.${CSS.CUSTOM_LIST_INPUT_GROUP}`); if (inputGroup) _clearAllInputErrorsInGroup(inputGroup); _resetEditStateInternal(); _currentModal.remove(); } _currentModal = null; _currentModalContent = null; }, openManageCustomOptions: function(manageType, currentSettingsRef, PREDEFINED_OPTIONS_REF, onModalCompleteCallback) { const modalConfigs = { 'site': () => ({ modalTitleKey: 'manageSitesTitle', customListId: IDS.SITES_LIST, customItemsArrayKey: 'favoriteSites', textPKey: 'modal_placeholder_name', valPKey: 'modal_placeholder_domain', hintKey: 'modal_hint_domain', fmtKey: 'modal_tooltip_domain', }), 'language': () => ({ modalTitleKey: 'manageLanguagesTitle', customListId: IDS.LANG_LIST, customItemsArrayKey: 'customLanguages', textPKey: 'modal_placeholder_text', valPKey: 'modal_placeholder_value', hintKey: 'modal_hint_language', fmtKey: 'modal_tooltip_language', predefinedOptionTypeKey: 'language', predefinedTypeNameKey: 'section_language', }), 'country': () => ({ modalTitleKey: 'manageCountriesTitle', customListId: IDS.COUNTRIES_LIST, customItemsArrayKey: 'customCountries', textPKey: 'modal_placeholder_text', valPKey: 'modal_placeholder_value', hintKey: 'modal_hint_country', fmtKey: 'modal_tooltip_country', predefinedOptionTypeKey: 'country', predefinedTypeNameKey: 'section_country', }), 'time': () => ({ modalTitleKey: 'manageTimeRangesTitle', customListId: IDS.TIME_LIST, customItemsArrayKey: 'customTimeRanges', textPKey: 'modal_placeholder_text', valPKey: 'modal_placeholder_value', hintKey: 'modal_hint_time', fmtKey: 'modal_tooltip_time', predefinedOptionTypeKey: 'time', predefinedTypeNameKey: 'section_time', }), 'filetype': () => ({ modalTitleKey: 'manageFileTypesTitle', customListId: IDS.FT_LIST, customItemsArrayKey: 'customFiletypes', textPKey: 'modal_placeholder_text', valPKey: 'modal_placeholder_value', hintKey: 'modal_hint_filetype', fmtKey: 'modal_tooltip_filetype', predefinedOptionTypeKey: 'filetype', predefinedTypeNameKey: 'section_filetype', }), }; const getConfigFn = modalConfigs[manageType]; if (!getConfigFn) return; const config = getConfigFn(); const tempCustomItems = JSON.parse(JSON.stringify(currentSettingsRef[config.customItemsArrayKey] || [])); let contentHTML = ''; if (config.predefinedOptionTypeKey && config.predefinedTypeNameKey) { const enabledValues = new Set(currentSettingsRef.enabledPredefinedOptions[config.predefinedOptionTypeKey] || []); contentHTML += _createPredefinedListHTML(config.predefinedOptionTypeKey, config.predefinedTypeNameKey, PREDEFINED_OPTIONS_REF, enabledValues) + '<hr>'; } contentHTML += _createCustomListHTML(config.customListId, config.textPKey, config.valPKey, config.hintKey, config.fmtKey); const modalContentElement = this.show(config.modalTitleKey, contentHTML, (modalContent) => { let newEnabledPredefined = null; if (config.predefinedOptionTypeKey) { newEnabledPredefined = []; modalContent.querySelectorAll(`.predefined-options-list input[data-option-type="${config.predefinedOptionTypeKey}"]:checked`).forEach(cb => newEnabledPredefined.push(cb.value)); } onModalCompleteCallback(tempCustomItems, newEnabledPredefined, config.customItemsArrayKey, config.predefinedOptionTypeKey); }, currentSettingsRef.theme); if (modalContentElement) { if (config.customListId && getListMapping(config.customListId)) { const mapping = getListMapping(config.customListId); mapping.populateFn(tempCustomItems, modalContentElement); } _bindModalContentEventsInternal(modalContentElement, tempCustomItems); } }, resetEditStateGlobally: function() { _resetEditStateInternal(_currentModalContent || document); }, isModalOpen: function() { return !!_currentModal; } }; })();
    const SettingsManager = (function() { /* ... (same content as previous complete code block, including all personalization settings logic) ... */ let _settingsWindow = null; let _settingsOverlay = null; let _currentSettings = {}; let _settingsBackup = {}; let _defaultSettingsRef = null; let _isInitialized = false; let _applySettingsToSidebar_cb = ()=>{}; let _buildSidebarUI_cb = ()=>{}; let _applySectionCollapseStates_cb = ()=>{}; let _initMenuCommands_cb = ()=>{}; let _renderSectionOrderList_ext_cb = ()=>{}; function _createPaneGeneralHTML_internal() { let langOpts = LocalizationService.getAvailableLocales().map(lc => { let dn; if (lc==='auto') dn=_( 'settings_language_auto'); else { try {dn=new Intl.DisplayNames([lc],{type:'language'}).of(lc);dn=dn.charAt(0).toUpperCase()+dn.slice(1);}catch(e){dn=lc;}dn=`${dn} (${lc})`;} return `<option value="${lc}">${dn}</option>`;}).join(''); return `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_INTERFACE_LANGUAGE}">${_('settings_interface_language')}</label><select id="${IDS.SETTING_INTERFACE_LANGUAGE}">${langOpts}</select></div>` + `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_SECTION_MODE}">${_('settings_section_mode')}</label><select id="${IDS.SETTING_SECTION_MODE}"><option value="remember">${_('settings_section_mode_remember')}</option><option value="expandAll">${_('settings_section_mode_expand')}</option><option value="collapseAll">${_('settings_section_mode_collapse')}</option></select><div style="margin-top:0.6em;"><input type="checkbox" id="${IDS.SETTING_ACCORDION}"><label for="${IDS.SETTING_ACCORDION}" class="${CSS.INLINE_LABEL}">${_('settings_accordion_mode')}</label></div></div>` + `<div class="${CSS.SETTING_ITEM}"><input type="checkbox" id="${IDS.SETTING_DRAGGABLE}"><label for="${IDS.SETTING_DRAGGABLE}" class="${CSS.INLINE_LABEL}">${_('settings_enable_drag')}</label></div>` + `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_RESET_LOCATION}">${_('settings_reset_button_location')}</label><select id="${IDS.SETTING_RESET_LOCATION}"><option value="tools">${_('settings_location_tools')}</option><option value="topBlock">${_('settings_location_top')}</option><option value="header">${_('settings_location_header')}</option><option value="none">${_('settings_location_hide')}</option></select></div>` + `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_VERBATIM_LOCATION}">${_('settings_verbatim_button_location')}</label><select id="${IDS.SETTING_VERBATIM_LOCATION}"><option value="tools">${_('settings_location_tools')}</option><option value="topBlock">${_('settings_location_top')}</option><option value="header">${_('settings_location_header')}</option><option value="none">${_('settings_location_hide')}</option></select></div>` + `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_ADV_SEARCH_LOCATION}">${_('settings_adv_search_location')}</label><select id="${IDS.SETTING_ADV_SEARCH_LOCATION}"><option value="tools">${_('settings_location_tools')}</option><option value="topBlock">${_('settings_location_top')}</option><option value="header">${_('settings_location_header')}</option><option value="none">${_('settings_location_hide')}</option></select></div>`+ `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_PERSONALIZE_LOCATION}">${_('settings_personalize_button_location')}</label><select id="${IDS.SETTING_PERSONALIZE_LOCATION}"><option value="tools">${_('settings_location_tools')}</option><option value="topBlock">${_('settings_location_top')}</option><option value="header">${_('settings_location_header')}</option><option value="none">${_('settings_location_hide')}</option></select></div>`;} function _createPaneAppearanceHTML_internal() { return `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_WIDTH}">${_('settings_sidebar_width')}</label><span class="${CSS.RANGE_HINT}">${_('settings_width_range_hint')}</span><input type="range" id="${IDS.SETTING_WIDTH}" min="90" max="270" step="5"><span class="${CSS.RANGE_VALUE}"></span></div>` + `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_FONT_SIZE}">${_('settings_font_size')}</label><span class="${CSS.RANGE_HINT}">${_('settings_font_size_range_hint')}</span><input type="range" id="${IDS.SETTING_FONT_SIZE}" min="8" max="24" step="0.5"><span class="${CSS.RANGE_VALUE}"></span></div>` + `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_HEADER_ICON_SIZE}">${_('settings_header_icon_size')}</label><span class="${CSS.RANGE_HINT}">${_('settings_header_icon_size_range_hint')}</span><input type="range" id="${IDS.SETTING_HEADER_ICON_SIZE}" min="8" max="32" step="0.5"><span class="${CSS.RANGE_VALUE}"></span></div>` + `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_VERTICAL_SPACING}">${_('settings_vertical_spacing')}</label><span class="${CSS.RANGE_HINT}">${_('settings_vertical_spacing_range_hint')}</span><input type="range" id="${IDS.SETTING_VERTICAL_SPACING}" min="0.05" max="1.5" step="0.05"><span class="${CSS.RANGE_VALUE}"></span></div>` + `<div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_THEME}">${_('settings_theme')}</label><select id="${IDS.SETTING_THEME}"><option value="system">${_('settings_theme_system')}</option><option value="light">${_('settings_theme_light')}</option><option value="dark">${_('settings_theme_dark')}</option><option value="minimal-light">${_('settings_theme_minimal_light')}</option><option value="minimal-dark">${_('settings_theme_minimal_dark')}</option></select></div><div class="${CSS.SETTING_ITEM}"><input type="checkbox" id="${IDS.SETTING_HOVER}"><label for="${IDS.SETTING_HOVER}" class="${CSS.INLINE_LABEL}">${_('settings_hover_mode')}</label><div style="margin-top:0.8em;padding-left:1.5em;"><label for="${IDS.SETTING_OPACITY}" style="display:block;margin-bottom:0.4em;font-weight:normal;">${_('settings_idle_opacity')}</label><span class="${CSS.RANGE_HINT}" style="width:auto;display:inline-block;margin-right:1em;">${_('settings_opacity_range_hint')}</span><input type="range" id="${IDS.SETTING_OPACITY}" min="0.1" max="1.0" step="0.05" style="width:calc(100% - 18em);vertical-align:middle;display:inline-block;"><span class="${CSS.RANGE_VALUE}" style="display:inline-block;min-width:3em;text-align:right;vertical-align:middle;"></span></div></div><div class="${CSS.SETTING_ITEM}"><label for="${IDS.SETTING_COUNTRY_DISPLAY_MODE}">${_('settings_country_display')}</label><select id="${IDS.SETTING_COUNTRY_DISPLAY_MODE}"><option value="iconAndText">${_('settings_country_display_icontext')}</option><option value="textOnly">${_('settings_country_display_text')}</option><option value="iconOnly">${_('settings_country_display_icon')}</option></select></div>`;} function _createPaneFeaturesHTML_internal() { const visItemsHTML = ALL_SECTION_DEFINITIONS.map(def=>{const dn=_(def.titleKey)||def.id;return `<div class="${CSS.SETTING_ITEM} ${CSS.SIMPLE_ITEM}"><input type="checkbox" id="setting-visible-${def.id}" data-${DATA_ATTR.SECTION_ID}="${def.id}"><label for="setting-visible-${def.id}" class="${CSS.INLINE_LABEL}">${dn}</label></div>`;}).join(''); return `<p>${_('settings_visible_sections')}</p>${visItemsHTML}<hr style="margin:1.2em 0;"><p style="font-weight:bold;margin-bottom:0.5em;">${_('settings_section_order')}</p><p style="font-size:0.9em;margin-top:-0.3em;margin-bottom:0.7em;">${_('settings_section_order_hint')}</p><ul id="${IDS.SIDEBAR_SECTION_ORDER_LIST}" class="${CSS.SECTION_ORDER_LIST}"></ul>`;} function _createPaneCustomHTML_internal() { return `<div class="${CSS.SETTING_ITEM}"><p>${_('settings_custom_intro')}</p><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="site">${_('settings_manage_sites_button')}</button></div><div class="${CSS.SETTING_ITEM}"><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="language">${_('settings_manage_languages_button')}</button></div><div class="${CSS.SETTING_ITEM}"><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="country">${_('settings_manage_countries_button')}</button></div><div class="${CSS.SETTING_ITEM}"><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="time">${_('settings_manage_time_ranges_button')}</button></div><div class="${CSS.SETTING_ITEM}"><button class="${CSS.MANAGE_CUSTOM_BUTTON}" data-${DATA_ATTR.MANAGE_TYPE}="filetype">${_('settings_manage_file_types_button')}</button></div>`;}
        function _populateSliderSetting_internal(win,id,value,formatFn=(val)=>val){const i=win.querySelector(`#${id}`);if(i){i.value=value;let vs=i.parentNode.querySelector(`.${CSS.RANGE_VALUE}`);if(vs&&vs.classList.contains(CSS.RANGE_VALUE)){vs.textContent=formatFn(value);}}}
        function _populateGeneralSettings_internal(win,s){ const lS=win.querySelector(`#${IDS.SETTING_INTERFACE_LANGUAGE}`);if(lS)lS.value=s.interfaceLanguage;const sMS=win.querySelector(`#${IDS.SETTING_SECTION_MODE}`),acC=win.querySelector(`#${IDS.SETTING_ACCORDION}`);if(sMS&&acC){sMS.value=s.sectionDisplayMode;const iRM=s.sectionDisplayMode==='remember';acC.disabled=!iRM;acC.checked=iRM?s.accordionMode:false;} const dC=win.querySelector(`#${IDS.SETTING_DRAGGABLE}`);if(dC)dC.checked=s.draggableHandleEnabled;const rLS=win.querySelector(`#${IDS.SETTING_RESET_LOCATION}`);if(rLS)rLS.value=s.resetButtonLocation;const vLS=win.querySelector(`#${IDS.SETTING_VERBATIM_LOCATION}`);if(vLS)vLS.value=s.verbatimButtonLocation;const aSLS=win.querySelector(`#${IDS.SETTING_ADV_SEARCH_LOCATION}`);if(aSLS)aSLS.value=s.advancedSearchLinkLocation; const pznLS = win.querySelector(`#${IDS.SETTING_PERSONALIZE_LOCATION}`); if (pznLS) pznLS.value = s.personalizationButtonLocation;}
        function _populateAppearanceSettings_internal(win,s){_populateSliderSetting_internal(win,IDS.SETTING_WIDTH,s.sidebarWidth);_populateSliderSetting_internal(win,IDS.SETTING_FONT_SIZE,s.fontSize,v=>parseFloat(v).toFixed(1));_populateSliderSetting_internal(win,IDS.SETTING_HEADER_ICON_SIZE,s.headerIconSize,v=>parseFloat(v).toFixed(1));_populateSliderSetting_internal(win,IDS.SETTING_VERTICAL_SPACING,s.verticalSpacingMultiplier,v=>`x ${parseFloat(v).toFixed(2)}`);_populateSliderSetting_internal(win,IDS.SETTING_OPACITY,s.idleOpacity,v=>parseFloat(v).toFixed(2));const tS=win.querySelector(`#${IDS.SETTING_THEME}`);if(tS)tS.value=s.theme;const cDS=win.querySelector(`#${IDS.SETTING_COUNTRY_DISPLAY_MODE}`);if(cDS)cDS.value=s.countryDisplayMode;const hC=win.querySelector(`#${IDS.SETTING_HOVER}`),oI=win.querySelector(`#${IDS.SETTING_OPACITY}`);if(hC&&oI){hC.checked=s.hoverMode;const iHE=s.hoverMode;oI.disabled=!iHE;const oC=oI.closest('div');if(oC){oC.style.opacity=iHE?'1':'0.6';oC.style.pointerEvents=iHE?'auto':'none';}}}
        function _populateFeatureSettings_internal(win,s,renderFn){win.querySelectorAll(`#${IDS.TAB_PANE_FEATURES} input[type="checkbox"][data-${DATA_ATTR.SECTION_ID}]`)?.forEach(cb=>{const sId=cb.getAttribute(`data-${DATA_ATTR.SECTION_ID}`);if(sId&&s.visibleSections.hasOwnProperty(sId)){cb.checked=s.visibleSections[sId];}else if(sId&&_defaultSettingsRef.visibleSections.hasOwnProperty(sId)){cb.checked=_defaultSettingsRef.visibleSections[sId]??false;}});renderFn(s);}
        function _initializeActiveSettingsTab_internal(){if(!_settingsWindow)return;const tC=_settingsWindow.querySelector(`.${CSS.SETTINGS_TABS}`),cC=_settingsWindow.querySelector(`.${CSS.SETTINGS_TAB_CONTENT}`);if(!tC||!cC)return;const aTB=tC.querySelector(`.${CSS.TAB_BUTTON}.${CSS.ACTIVE}`);const tT=(aTB&&aTB.dataset[DATA_ATTR.TAB])?aTB.dataset[DATA_ATTR.TAB]:'general';tC.querySelectorAll(`.${CSS.TAB_BUTTON}`).forEach(b=>b.classList.toggle(CSS.ACTIVE,b.dataset[DATA_ATTR.TAB]===tT));cC.querySelectorAll(`.${CSS.TAB_PANE}`).forEach(p=>p.classList.toggle(CSS.ACTIVE,p.dataset[DATA_ATTR.TAB]===tT));}
        function _loadFromStorage(){try{const s=GM_getValue(STORAGE_KEY,'{}');return JSON.parse(s||'{}');}catch(e){console.error(`${LOG_PREFIX} Error loading/parsing settings:`,e);return{};}}
        function _validateAndMergeSettings(saved){let nS=JSON.parse(JSON.stringify(_defaultSettingsRef));nS=Utils.mergeDeep(nS,saved);_validateAndMergeCoreSettings_internal(nS,saved,_defaultSettingsRef);_validateAndMergeAppearanceSettings_internal(nS,saved,_defaultSettingsRef);_validateAndMergeFeatureSettings_internal(nS,saved,_defaultSettingsRef);_validateAndMergeCustomLists_internal(nS,saved,_defaultSettingsRef);_validateAndMergePredefinedOptions_internal(nS,saved,_defaultSettingsRef);_finalizeSectionOrder_internal(nS,saved,_defaultSettingsRef);return nS;}
        function _validateAndMergeCoreSettings_internal(t,s,d){if(typeof t.sidebarPosition!=='object'||t.sidebarPosition===null||Array.isArray(t.sidebarPosition)){t.sidebarPosition=JSON.parse(JSON.stringify(d.sidebarPosition));}t.sidebarPosition.left=parseInt(t.sidebarPosition.left,10)||d.sidebarPosition.left;t.sidebarPosition.top=parseInt(t.sidebarPosition.top,10)||d.sidebarPosition.top;if(typeof t.sectionStates!=='object'||t.sectionStates===null||Array.isArray(t.sectionStates)){t.sectionStates={};}t.sidebarCollapsed=!!t.sidebarCollapsed;t.draggableHandleEnabled=typeof t.draggableHandleEnabled==='boolean'?t.draggableHandleEnabled:d.draggableHandleEnabled;t.interfaceLanguage=typeof s.interfaceLanguage==='string'?s.interfaceLanguage:d.interfaceLanguage;}
        function _validateAndMergeAppearanceSettings_internal(t,s,d){t.sidebarWidth=Utils.clamp(parseInt(t.sidebarWidth,10)||d.sidebarWidth,90,270);t.fontSize=Utils.clamp(parseFloat(t.fontSize)||d.fontSize,8,24);t.headerIconSize=Utils.clamp(parseFloat(t.headerIconSize)||d.headerIconSize,8,32);t.verticalSpacingMultiplier=Utils.clamp(parseFloat(t.verticalSpacingMultiplier)||d.verticalSpacingMultiplier,0.05,1.5);t.idleOpacity=Utils.clamp(parseFloat(t.idleOpacity)||d.idleOpacity,0.1,1.0);t.hoverMode=!!t.hoverMode;const vT=['system','light','dark','minimal-light','minimal-dark'];if(t.theme==='minimal')t.theme='minimal-light';else if(!vT.includes(t.theme))t.theme=d.theme;}
        function _validateAndMergeFeatureSettings_internal(t,s,d){if(typeof t.visibleSections!=='object'||t.visibleSections===null||Array.isArray(t.visibleSections)){t.visibleSections=JSON.parse(JSON.stringify(d.visibleSections));}const vSI=new Set(ALL_SECTION_DEFINITIONS.map(def=>def.id));Object.keys(d.visibleSections).forEach(id=>{if(!vSI.has(id)){console.warn(`${LOG_PREFIX} Invalid section ID in defaultSettings.visibleSections: ${id}`);}else if(typeof t.visibleSections[id]!=='boolean'){t.visibleSections[id]=d.visibleSections[id]??true;}});const vSM=['remember','expandAll','collapseAll'];if(!vSM.includes(t.sectionDisplayMode))t.sectionDisplayMode=d.sectionDisplayMode;t.accordionMode=!!t.accordionMode;const validButtonLocations=['header','topBlock','tools','none'];if(!validButtonLocations.includes(t.resetButtonLocation))t.resetButtonLocation=d.resetButtonLocation;if(!validButtonLocations.includes(t.verbatimButtonLocation))t.verbatimButtonLocation=d.verbatimButtonLocation;if(!validButtonLocations.includes(t.advancedSearchLinkLocation))t.advancedSearchLinkLocation=d.advancedSearchLinkLocation; if (!validButtonLocations.includes(t.personalizationButtonLocation)) { t.personalizationButtonLocation = d.personalizationButtonLocation; } const vCDM=['iconAndText','textOnly','iconOnly'];if(!vCDM.includes(t.countryDisplayMode))t.countryDisplayMode=d.countryDisplayMode;}
        function _validateAndMergeCustomLists_internal(t,s,d){const lK=['favoriteSites','customLanguages','customTimeRanges','customFiletypes','customCountries'];lK.forEach(k=>{t[k]=Array.isArray(t[k])?t[k].filter(i=>i&&typeof i.text==='string'&&typeof i[k==='favoriteSites'?'url':'value']==='string'&&i.text.trim()!==''&&i[k==='favoriteSites'?'url':'value'].trim()!==''):JSON.parse(JSON.stringify(d[k]));});}
        function _validateAndMergePredefinedOptions_internal(t,s,d){t.enabledPredefinedOptions=JSON.parse(JSON.stringify(d.enabledPredefinedOptions));const sEO=s.enabledPredefinedOptions;if(sEO&&typeof sEO==='object'){for(const type in d.enabledPredefinedOptions){if(PREDEFINED_OPTIONS[type]&&Array.isArray(sEO[type])){const vV=new Set(PREDEFINED_OPTIONS[type].map(opt=>opt.value));t.enabledPredefinedOptions[type]=sEO[type].filter(val=>typeof val==='string'&&vV.has(val));}}}for(const type in d.enabledPredefinedOptions){if(!Array.isArray(t.enabledPredefinedOptions[type])){t.enabledPredefinedOptions[type]=[...(d.enabledPredefinedOptions[type]||[])];}}}
        function _finalizeSectionOrder_internal(t,s,d){const fO=[];const cVOS=new Set();const vSI=new Set(ALL_SECTION_DEFINITIONS.map(def=>def.id));const oS=(Array.isArray(s.sidebarSectionOrder)&&s.sidebarSectionOrder.length>0)?s.sidebarSectionOrder:d.sidebarSectionOrder;oS.forEach(id=>{if(typeof id==='string'&&vSI.has(id)&&t.visibleSections[id]===true&&!cVOS.has(id)){fO.push(id);cVOS.add(id);}});d.sidebarSectionOrder.forEach(id=>{if(typeof id==='string'&&vSI.has(id)&&t.visibleSections[id]===true&&!cVOS.has(id)){fO.push(id);}});t.sidebarSectionOrder=fO;}
        const _sEH_internal = { [IDS.SETTING_WIDTH]:(t,vS)=>_hSLI(t,'sidebarWidth',vS,90,270,5), [IDS.SETTING_FONT_SIZE]:(t,vS)=>_hSLI(t,'fontSize',vS,8,24,0.5,v=>parseFloat(v).toFixed(1)), [IDS.SETTING_HEADER_ICON_SIZE]:(t,vS)=>_hSLI(t,'headerIconSize',vS,8,32,0.5,v=>parseFloat(v).toFixed(1)), [IDS.SETTING_VERTICAL_SPACING]:(t,vS)=>_hSLI(t,'verticalSpacingMultiplier',vS,0.05,1.5,0.05,v=>`x ${parseFloat(v).toFixed(2)}`), [IDS.SETTING_OPACITY]:(t,vS)=>_hSLI(t,'idleOpacity',vS,0.1,1.0,0.05,v=>parseFloat(v).toFixed(2)), [IDS.SETTING_INTERFACE_LANGUAGE]:(t)=>{const nL=t.value;if(_currentSettings.interfaceLanguage!==nL){_currentSettings.interfaceLanguage=nL;LocalizationService.updateActiveLocale(_currentSettings);_initMenuCommands_cb();publicApi.populateWindow();_buildSidebarUI_cb();}}, [IDS.SETTING_THEME]:(t)=>{_currentSettings.theme=t.value;_applySettingsToSidebar_cb(_currentSettings);}, [IDS.SETTING_HOVER]:(t)=>{_currentSettings.hoverMode=t.checked;const oI=_settingsWindow.querySelector(`#${IDS.SETTING_OPACITY}`);if(oI){const iHE=_currentSettings.hoverMode;oI.disabled=!iHE;const oC=oI.closest('div');if(oC){oC.style.opacity=iHE?'1':'0.6';oC.style.pointerEvents=iHE?'auto':'none';}}_applySettingsToSidebar_cb(_currentSettings);}, [IDS.SETTING_DRAGGABLE]:(t)=>{_currentSettings.draggableHandleEnabled=t.checked;_applySettingsToSidebar_cb(_currentSettings);DragManager.setDraggable(t.checked, sidebar, sidebar?.querySelector(`.${CSS.DRAG_HANDLE}`), _currentSettings, debouncedSaveSettings);}, [IDS.SETTING_ACCORDION]:(t)=>{const sMS=_settingsWindow.querySelector(`#${IDS.SETTING_SECTION_MODE}`);if(sMS?.value==='remember')_currentSettings.accordionMode=t.checked;else{t.checked=false;_currentSettings.accordionMode=false;}_applySettingsToSidebar_cb(_currentSettings);_applySectionCollapseStates_cb();}, [IDS.SETTING_SECTION_MODE]:(t)=>{_currentSettings.sectionDisplayMode=t.value;const aC=_settingsWindow.querySelector(`#${IDS.SETTING_ACCORDION}`);if(aC){const iRM=t.value==='remember';aC.disabled=!iRM;if(!iRM){aC.checked=false;_currentSettings.accordionMode=false;}else{aC.checked=_settingsBackup?.accordionMode??_currentSettings.accordionMode??_defaultSettingsRef.accordionMode;_currentSettings.accordionMode=aC.checked;}}_applySettingsToSidebar_cb(_currentSettings);_applySectionCollapseStates_cb();}, [IDS.SETTING_RESET_LOCATION]:(t)=>{_currentSettings.resetButtonLocation=t.value;_buildSidebarUI_cb();}, [IDS.SETTING_VERBATIM_LOCATION]:(t)=>{_currentSettings.verbatimButtonLocation=t.value;_buildSidebarUI_cb();}, [IDS.SETTING_ADV_SEARCH_LOCATION]:(t)=>{_currentSettings.advancedSearchLinkLocation=t.value;_buildSidebarUI_cb();}, [IDS.SETTING_PERSONALIZE_LOCATION]: (target) => { _currentSettings.personalizationButtonLocation = target.value; _buildSidebarUI_cb(); }, [IDS.SETTING_COUNTRY_DISPLAY_MODE]:(t)=>{_currentSettings.countryDisplayMode=t.value;_buildSidebarUI_cb();}, };
        function _hSLI(t,sK,vS,min,max,step,fFn=v=>v){const v=Utils.clamp((step===1||step===5)?parseInt(t.value,10):parseFloat(t.value),min,max);if(isNaN(v))_currentSettings[sK]=_defaultSettingsRef[sK];else _currentSettings[sK]=v;if(vS)vS.textContent=fFn(_currentSettings[sK]);_applySettingsToSidebar_cb(_currentSettings);}
        function _lUH_internal(e){const t=e.target;if(!t)return;const sI=t.id;const vS=(t.type==='range')?t.parentNode.querySelector(`.${CSS.RANGE_VALUE}`):null;if(_sEH_internal[sI]){if(t.type==='range')_sEH_internal[sI](t,vS);else _sEH_internal[sI](t);}}
        const publicApi = { initialize: function(dS, applyCb, buildCb, collapseCb, menuCb, renderOrderCb) { if(_isInitialized) return; _defaultSettingsRef=dS; _applySettingsToSidebar_cb=applyCb; _buildSidebarUI_cb=buildCb; _applySectionCollapseStates_cb=collapseCb; _initMenuCommands_cb=menuCb; _renderSectionOrderList_ext_cb=renderOrderCb; this.load(); this.buildSkeleton(); _isInitialized=true; }, load: function(){const s=_loadFromStorage();_currentSettings=_validateAndMergeSettings(s);LocalizationService.updateActiveLocale(_currentSettings);}, save: function(lC='SaveBtn'){try{GM_setValue(STORAGE_KEY,JSON.stringify(_currentSettings));console.log(`${LOG_PREFIX} Settings saved by SM${lC?` (${lC})`:''}.`);_settingsBackup=JSON.parse(JSON.stringify(_currentSettings));}catch(e){console.error(`${LOG_PREFIX} SM save error:`,e);NotificationManager.show('alert_generic_error', { context: 'saving settings' }, 'error');}}, reset: function(){if(confirm(_('confirm_reset_settings'))){_currentSettings=JSON.parse(JSON.stringify(_defaultSettingsRef));if(!_currentSettings.sidebarSectionOrder||_currentSettings.sidebarSectionOrder.length===0){_currentSettings.sidebarSectionOrder=[..._defaultSettingsRef.sidebarSectionOrder];}LocalizationService.updateActiveLocale(_currentSettings);this.populateWindow();_applySettingsToSidebar_cb(_currentSettings);_buildSidebarUI_cb();_initMenuCommands_cb();_showGlobalMessage('alert_settings_reset_success',{},'success',4000);}}, resetAllFromMenu: function(){if(confirm(_('confirm_reset_all_menu'))){try{GM_setValue(STORAGE_KEY,JSON.stringify(_defaultSettingsRef));alert(_('alert_reset_all_menu_success'));}catch(e){_showGlobalMessage('alert_reset_all_menu_fail',{},'error',0);}}}, getCurrentSettings: function(){return _currentSettings;}, buildSkeleton: function(){if(_settingsWindow)return;_settingsOverlay=document.createElement('div');_settingsOverlay.id=IDS.SETTINGS_OVERLAY;_settingsWindow=document.createElement('div');_settingsWindow.id=IDS.SETTINGS_WINDOW;const h=document.createElement('div');h.classList.add(CSS.SETTINGS_HEADER);h.innerHTML=`<h3>${_('settingsTitle')}</h3><button class="${CSS.SETTINGS_CLOSE_BTN}" title="${_('settings_close_button_title')}">${SVG_ICONS.close}</button>`;const mB=document.createElement('div');mB.id=IDS.SETTINGS_MESSAGE_BAR;mB.classList.add(CSS.MESSAGE_BAR);mB.style.display='none';const ts=document.createElement('div');ts.classList.add(CSS.SETTINGS_TABS);ts.innerHTML=`<button class="${CSS.TAB_BUTTON} ${CSS.ACTIVE}" data-${DATA_ATTR.TAB}="general">${_('settings_tab_general')}</button> <button class="${CSS.TAB_BUTTON}" data-${DATA_ATTR.TAB}="appearance">${_('settings_tab_appearance')}</button> <button class="${CSS.TAB_BUTTON}" data-${DATA_ATTR.TAB}="features">${_('settings_tab_features')}</button> <button class="${CSS.TAB_BUTTON}" data-${DATA_ATTR.TAB}="custom">${_('settings_tab_custom')}</button>`;const c=document.createElement('div');c.classList.add(CSS.SETTINGS_TAB_CONTENT);c.innerHTML=`<div class="${CSS.TAB_PANE} ${CSS.ACTIVE}" data-${DATA_ATTR.TAB}="general" id="${IDS.TAB_PANE_GENERAL}"></div><div class="${CSS.TAB_PANE}" data-${DATA_ATTR.TAB}="appearance" id="${IDS.TAB_PANE_APPEARANCE}"></div><div class="${CSS.TAB_PANE}" data-${DATA_ATTR.TAB}="features" id="${IDS.TAB_PANE_FEATURES}"></div><div class="${CSS.TAB_PANE}" data-${DATA_ATTR.TAB}="custom" id="${IDS.TAB_PANE_CUSTOM}"></div>`;const f=document.createElement('div');f.classList.add(CSS.SETTINGS_FOOTER);f.innerHTML=`<button class="${CSS.RESET_BUTTON}">${_('settings_reset_all_button')}</button><button class="${CSS.CANCEL_BUTTON}">${_('settings_cancel_button')}</button><button class="${CSS.SAVE_BUTTON}">${_('settings_save_button')}</button>`;_settingsWindow.appendChild(h);_settingsWindow.appendChild(mB);_settingsWindow.appendChild(ts);_settingsWindow.appendChild(c);_settingsWindow.appendChild(f);_settingsOverlay.appendChild(_settingsWindow);document.body.appendChild(_settingsOverlay);this.bindEvents();}, populateWindow: function(){if(!_settingsWindow)return;try{_settingsWindow.querySelector(`.${CSS.SETTINGS_HEADER} h3`).textContent=_( 'settingsTitle');_settingsWindow.querySelector(`.${CSS.SETTINGS_CLOSE_BTN}`).title=_( 'settings_close_button_title');_settingsWindow.querySelector(`button[data-${DATA_ATTR.TAB}="general"]`).textContent=_( 'settings_tab_general');_settingsWindow.querySelector(`button[data-${DATA_ATTR.TAB}="appearance"]`).textContent=_( 'settings_tab_appearance');_settingsWindow.querySelector(`button[data-${DATA_ATTR.TAB}="features"]`).textContent=_( 'settings_tab_features');_settingsWindow.querySelector(`button[data-${DATA_ATTR.TAB}="custom"]`).textContent=_( 'settings_tab_custom');_settingsWindow.querySelector(`.${CSS.RESET_BUTTON}`).textContent=_( 'settings_reset_all_button');_settingsWindow.querySelector(`.${CSS.CANCEL_BUTTON}`).textContent=_( 'settings_cancel_button');_settingsWindow.querySelector(`.${CSS.SAVE_BUTTON}`).textContent=_( 'settings_save_button');const pG=_settingsWindow.querySelector(`#${IDS.TAB_PANE_GENERAL}`);if(pG)pG.innerHTML=_createPaneGeneralHTML_internal();const pA=_settingsWindow.querySelector(`#${IDS.TAB_PANE_APPEARANCE}`);if(pA)pA.innerHTML=_createPaneAppearanceHTML_internal();const pF=_settingsWindow.querySelector(`#${IDS.TAB_PANE_FEATURES}`);if(pF)pF.innerHTML=_createPaneFeaturesHTML_internal();const pC=_settingsWindow.querySelector(`#${IDS.TAB_PANE_CUSTOM}`);if(pC)pC.innerHTML=_createPaneCustomHTML_internal();_populateGeneralSettings_internal(_settingsWindow,_currentSettings);_populateAppearanceSettings_internal(_settingsWindow,_currentSettings);_populateFeatureSettings_internal(_settingsWindow,_currentSettings, _renderSectionOrderList_ext_cb);ModalManager.resetEditStateGlobally();_initializeActiveSettingsTab_internal();this.bindLiveUpdateEvents();this.bindFeaturesTabEvents(); }catch(e){_showGlobalMessage('alert_init_fail',{scriptName:SCRIPT_INTERNAL_NAME,error:"Settings UI pop err"},'error',0);}}, show: function(){if(!_settingsOverlay||!_settingsWindow)return;_settingsBackup=JSON.parse(JSON.stringify(_currentSettings));LocalizationService.updateActiveLocale(_currentSettings);this.populateWindow();applyThemeToElement(_settingsWindow,_currentSettings.theme);applyThemeToElement(_settingsOverlay,_currentSettings.theme);_settingsOverlay.style.display='flex';}, hide: function(isC=false){if(!_settingsOverlay)return;ModalManager.resetEditStateGlobally();if(ModalManager.isModalOpen())ModalManager.hide(true);_settingsOverlay.style.display='none';const mB=document.getElementById(IDS.SETTINGS_MESSAGE_BAR);if(mB)mB.style.display='none';if(isC&&_settingsBackup&&Object.keys(_settingsBackup).length>0){_currentSettings=JSON.parse(JSON.stringify(_settingsBackup));LocalizationService.updateActiveLocale(_currentSettings);this.populateWindow();_applySettingsToSidebar_cb(_currentSettings);_buildSidebarUI_cb();_initMenuCommands_cb();}else if(isC){console.warn(`${LOG_PREFIX} SM: Cancelled, no backup.`);}}, bindEvents: function(){if(!_settingsWindow||_settingsWindow.dataset.eventsBound==='true')return;_settingsWindow.querySelector(`.${CSS.SETTINGS_CLOSE_BTN}`)?.addEventListener('click',()=>this.hide(true));_settingsWindow.querySelector(`.${CSS.CANCEL_BUTTON}`)?.addEventListener('click',()=>this.hide(true));_settingsWindow.querySelector(`.${CSS.SAVE_BUTTON}`)?.addEventListener('click',()=>{this.save();LocalizationService.updateActiveLocale(_currentSettings);_initMenuCommands_cb();_buildSidebarUI_cb();this.hide(false);});_settingsWindow.querySelector(`.${CSS.RESET_BUTTON}`)?.addEventListener('click',()=>this.reset());const tC=_settingsWindow.querySelector(`.${CSS.SETTINGS_TABS}`);if(tC){tC.addEventListener('click',e=>{const tg=e.target.closest(`.${CSS.TAB_BUTTON}`);if(tg&&!tg.classList.contains(CSS.ACTIVE)){ModalManager.resetEditStateGlobally();const tT=tg.dataset[DATA_ATTR.TAB];if(!tT)return;tC.querySelectorAll(`.${CSS.TAB_BUTTON}`).forEach(b=>b.classList.remove(CSS.ACTIVE));tg.classList.add(CSS.ACTIVE);_settingsWindow.querySelector(`.${CSS.SETTINGS_TAB_CONTENT}`)?.querySelectorAll(`.${CSS.TAB_PANE}`)?.forEach(p=>p.classList.remove(CSS.ACTIVE));_settingsWindow.querySelector(`.${CSS.SETTINGS_TAB_CONTENT} .${CSS.TAB_PANE}[data-${DATA_ATTR.TAB}="${tT}"]`)?.classList.add(CSS.ACTIVE);}});}_settingsWindow.dataset.eventsBound='true';const cTP=_settingsWindow.querySelector(`#${IDS.TAB_PANE_CUSTOM}`);if(cTP){cTP.addEventListener('click',(e)=>{const b=e.target.closest(`button.${CSS.MANAGE_CUSTOM_BUTTON}`);if(b){const mT=b.dataset[DATA_ATTR.MANAGE_TYPE];if(mT){ModalManager.openManageCustomOptions(mT,_currentSettings,PREDEFINED_OPTIONS,(tCI,nEP,cIK,pOTK)=>{if(cIK){_currentSettings[cIK]=tCI;}if(pOTK&&nEP){_currentSettings.enabledPredefinedOptions[pOTK]=nEP;}_buildSidebarUI_cb();this.populateWindow();});}}});}}, bindLiveUpdateEvents: function(){if(!_settingsWindow)return;_settingsWindow.querySelectorAll('input[type="range"]').forEach(el=>{el.removeEventListener('input',_lUH_internal);el.addEventListener('input',_lUH_internal);});_settingsWindow.querySelectorAll('select, input[type="checkbox"]:not([data-section-id])').forEach(el=>{if(_sEH_internal[el.id]){el.removeEventListener('change',_lUH_internal);el.addEventListener('change',_lUH_internal);}});}, bindFeaturesTabEvents: function() {const fP=_settingsWindow.querySelector(`#${IDS.TAB_PANE_FEATURES}`);if(fP){fP.querySelectorAll(`input[type="checkbox"][data-${DATA_ATTR.SECTION_ID}]`).forEach(cb=>{cb.removeEventListener('change',this._handleVisibleSectionChange);cb.addEventListener('change',this._handleVisibleSectionChange.bind(this));});const oLE=fP.querySelector(`#${IDS.SIDEBAR_SECTION_ORDER_LIST}`);if(oLE){oLE.removeEventListener('click',this._handleSectionOrderChange);oLE.addEventListener('click',this._handleSectionOrderChange.bind(this));}}}, _handleVisibleSectionChange: function(e){const t=e.target;const sI=t.getAttribute(`data-${DATA_ATTR.SECTION_ID}`);if(sI&&_currentSettings.visibleSections.hasOwnProperty(sI)){_currentSettings.visibleSections[sI]=t.checked;_finalizeSectionOrder_internal(_currentSettings,_currentSettings,_defaultSettingsRef);_renderSectionOrderList_ext_cb(_currentSettings);_buildSidebarUI_cb();}}, _handleSectionOrderChange: function(e){const tB=e.target.closest('button.move-section-up, button.move-section-down');if(!tB)return;const lI=tB.closest('li[data-section-id]');if(!lI)return;const sITM=lI.dataset.sectionId;let vOS=_currentSettings.sidebarSectionOrder.filter(id=>_currentSettings.visibleSections[id]);const cIV=vOS.indexOf(sITM);if(cIV===-1)return;let nIV=-1;if(tB.classList.contains('move-section-up')&&cIV>0){nIV=cIV-1;}else if(tB.classList.contains('move-section-down')&&cIV<vOS.length-1){nIV=cIV+1;}if(nIV!==-1){[vOS[cIV],vOS[nIV]]=[vOS[nIV],vOS[cIV]];let nFO=[...vOS];_defaultSettingsRef.sidebarSectionOrder.forEach(id=>{if(!_currentSettings.visibleSections[id]&&!nFO.includes(id)){}});_currentSettings.sidebarSectionOrder=[...nFO];_defaultSettingsRef.sidebarSectionOrder.forEach(id=>{if(!_currentSettings.visibleSections[id]&&!_currentSettings.sidebarSectionOrder.includes(id)){_currentSettings.sidebarSectionOrder.push(id);}});_renderSectionOrderList_ext_cb(_currentSettings);}} }; return publicApi; })();
    const DragManager = (function() { /* ... (same as before) ... */ let _isDragging = false; let _dragStartX, _dragStartY, _sidebarStartX, _sidebarStartY; let _sidebarElement, _handleElement; let _settingsManagerRef, _saveCallbackRef; function _getEventCoordinates(e) { return (e.touches && e.touches.length > 0) ? { x: e.touches[0].clientX, y: e.touches[0].clientY } : { x: e.clientX, y: e.clientY }; } function _startDrag(e) { const currentSettings = _settingsManagerRef.getCurrentSettings(); if (!currentSettings.draggableHandleEnabled || currentSettings.sidebarCollapsed || (e.type === 'mousedown' && e.button !== 0)) { return; } e.preventDefault(); _isDragging = true; const coords = _getEventCoordinates(e); _dragStartX = coords.x; _dragStartY = coords.y; _sidebarStartX = _sidebarElement.offsetLeft; _sidebarStartY = _sidebarElement.offsetTop; _sidebarElement.style.cursor = 'grabbing'; _sidebarElement.style.userSelect = 'none'; document.body.style.cursor = 'grabbing'; } function _drag(e) { if (!_isDragging) return; e.preventDefault(); const coords = _getEventCoordinates(e); const dx = coords.x - _dragStartX; const dy = coords.y - _dragStartY; let newLeft = _sidebarStartX + dx; let newTop = _sidebarStartY + dy; const maxLeft = window.innerWidth - (_sidebarElement?.offsetWidth ?? 0); const maxTop = window.innerHeight - (_sidebarElement?.offsetHeight ?? 0); newLeft = Utils.clamp(newLeft, 0, maxLeft); newTop = Utils.clamp(newTop, MIN_SIDEBAR_TOP_POSITION, maxTop); if (_sidebarElement) { _sidebarElement.style.left = `${newLeft}px`; _sidebarElement.style.top = `${newTop}px`; } } function _stopDrag() { if (_isDragging) { _isDragging = false; if (_sidebarElement) { _sidebarElement.style.cursor = 'default'; _sidebarElement.style.userSelect = ''; } document.body.style.cursor = ''; const currentSettings = _settingsManagerRef.getCurrentSettings(); if (!currentSettings.sidebarPosition) currentSettings.sidebarPosition = {}; currentSettings.sidebarPosition.left = _sidebarElement.offsetLeft; currentSettings.sidebarPosition.top = _sidebarElement.offsetTop; if (typeof _saveCallbackRef === 'function') { _saveCallbackRef('Drag Stop'); } } } return { init: function(sidebarEl, handleEl, settingsMgr, saveCb) { _sidebarElement = sidebarEl; _handleElement = handleEl; _settingsManagerRef = settingsMgr; _saveCallbackRef = saveCb; if (_handleElement) { _handleElement.addEventListener('mousedown', _startDrag); _handleElement.addEventListener('touchstart', _startDrag, { passive: false }); } document.addEventListener('mousemove', _drag); document.addEventListener('touchmove', _drag, { passive: false }); document.addEventListener('mouseup', _stopDrag); document.addEventListener('touchend', _stopDrag); document.addEventListener('touchcancel', _stopDrag); }, setDraggable: function(isEnabled, sidebarEl, handleEl) { _sidebarElement = sidebarEl; _handleElement = handleEl; if (_handleElement) { _handleElement.style.display = isEnabled ? 'block' : 'none'; } } }; })();
    const URLActionManager = (function() { /* ... (same as before, with personalization functions and debug logs commented out) ... */
        function _getURLObject() { try { return new URL(window.location.href); } catch (e) { console.error(`${LOG_PREFIX} Error creating URL object: `, e); return null; }}
        function _navigateTo(url) { const urlString = url.toString(); /* console.log(`${LOG_PREFIX} Navigating to: ${urlString}`); */ window.location.href = urlString; }
        function _setSearchParam(urlObj, paramName, value) { urlObj.searchParams.set(paramName, value); }
        function _deleteSearchParam(urlObj, paramName) { urlObj.searchParams.delete(paramName); }
        function _getTbsParts(urlObj) { const tbs = urlObj.searchParams.get('tbs'); return tbs ? tbs.split(',').filter(p => p.trim() !== '') : []; }
        function _setTbsParam(urlObj, tbsPartsArray) { const newTbsValue = tbsPartsArray.join(','); if (newTbsValue) { _setSearchParam(urlObj, 'tbs', newTbsValue); } else { _deleteSearchParam(urlObj, 'tbs'); }}
        return {
            triggerResetFilters: function() { try { const u = _getURLObject(); if (!u) return; const q = u.searchParams.get('q') || ''; const nP = new URLSearchParams(); const cQ = q.replace(/\s*site:[\w.-]+\s*/g, ' ').trim(); if (cQ) { nP.set('q', cQ); } u.search = nP.toString(); _navigateTo(u); } catch (e) { NotificationManager.show('alert_error_resetting_filters', {}, 'error', 5000); }},
            triggerToggleVerbatim: function() { try { const u = _getURLObject(); if (!u) return; let tP = _getTbsParts(u); const vP = 'li:1'; const iCA = tP.includes(vP); tP = tP.filter(p => p !== vP); if (!iCA) { tP.push(vP); } _setTbsParam(u, tP); _navigateTo(u); } catch (e) { NotificationManager.show('alert_error_toggling_verbatim', {}, 'error', 5000); }},
            isPersonalizationActive: function() { try { const currentUrl = _getURLObject(); if (!currentUrl) { return true; } return currentUrl.searchParams.get('pws') !== '0'; } catch (e) { console.warn(`${LOG_PREFIX} [URLActionManager.isPersonalizationActive] Error checking personalization status:`, e); return true; } },
            triggerTogglePersonalization: function() { /* console.log(`${LOG_PREFIX} [URLActionManager.triggerTogglePersonalization] Clicked!`); */ try { const u = _getURLObject(); if (!u) { return; } const personalizationCurrentlyActive = URLActionManager.isPersonalizationActive(); if (personalizationCurrentlyActive) { _setSearchParam(u, 'pws', '0'); } else { _deleteSearchParam(u, 'pws'); } _navigateTo(u); } catch (e) { NotificationManager.show('alert_error_toggling_personalization', {}, 'error', 5000); console.error(`${LOG_PREFIX} [URLActionManager.triggerTogglePersonalization] Error:`, e); } },
            applyFilter: function(type, value) { try { const u = _getURLObject(); if (!u) return; let tP = _getTbsParts(u); let pTP; const isTimeFilter = ['qdr'].includes(type); const isStandaloneParam = ['lr', 'cr'].includes(type); const isFileType = type === 'filetype'; if (isTimeFilter) { pTP = tP.filter(p => !p.startsWith(`${type}:`) && !p.startsWith('cdr:') && !p.startsWith('cd_min:') && !p.startsWith('cd_max:')); if (value !== '') pTP.push(`${type}:${value}`); } else if (isFileType) { _deleteSearchParam(u, 'as_filetype'); _deleteSearchParam(u, 'filetype'); pTP = tP.filter(p => !p.startsWith('ft:') && !p.startsWith('aft:')); if (value !== '') _setSearchParam(u, 'as_filetype', value); } else if (isStandaloneParam) { _deleteSearchParam(u, type); if (value !== '') _setSearchParam(u, type, value); pTP = tP; } else { pTP = tP; } _setTbsParam(u, pTP); _navigateTo(u); } catch (e) { NotificationManager.show('alert_error_applying_filter', { type: type, value: value }, 'error', 5000); }},
            applySiteSearch: function(siteUrl) { if (!siteUrl) return; try { const u = _getURLObject(); if (!u) return; const q = u.searchParams.get('q') || ''; const qNS = q.replace(/\s*site:[\w.-]+\s*/g, ' ').trim(); const nQ = `${qNS} site:${siteUrl}`.trim(); _setSearchParam(u, 'q', nQ); _deleteSearchParam(u, 'tbs'); _deleteSearchParam(u, 'lr'); _deleteSearchParam(u, 'cr'); _deleteSearchParam(u, 'as_filetype'); _deleteSearchParam(u, 'filetype'); _navigateTo(u); } catch (e) { NotificationManager.show('alert_error_applying_site_search', { site: siteUrl }, 'error', 5000); }},
            clearSiteSearch: function() { try { const u = _getURLObject(); if (!u) return; const q = u.searchParams.get('q') || ''; const nQ = q.replace(/\s*site:[\w.-]+\s*/g, ' ').trim(); if (nQ) { _setSearchParam(u, 'q', nQ); } else { _deleteSearchParam(u, 'q'); } _navigateTo(u); } catch (e) { NotificationManager.show('alert_error_clearing_site_search', {}, 'error', 5000); }},
            isVerbatimActive: function() { try { const currentUrl = _getURLObject(); if (!currentUrl) return false; return /li:1/.test(currentUrl.searchParams.get('tbs') || ''); } catch (e) { console.warn(`${LOG_PREFIX} Error checking verbatim status:`, e); return false; }},
            applyDateRange: function(dateMinStr, dateMaxStr) { try { const url = _getURLObject(); if (!url) return; let dateTbsPart = 'cdr:1'; if (dateMinStr) { const [y, m, d] = dateMinStr.split('-'); dateTbsPart += `,cd_min:${m}/${d}/${y}`; } if (dateMaxStr) { const [y, m, d] = dateMaxStr.split('-'); dateTbsPart += `,cd_max:${m}/${d}/${y}`; } let tbsParts = _getTbsParts(url); let preservedTbsParts = tbsParts.filter(p => !p.startsWith('qdr:') && !p.startsWith('cdr:') && !p.startsWith('cd_min:') && !p.startsWith('cd_max:')); let newTbsParts = [...preservedTbsParts, dateTbsPart]; _setTbsParam(url, newTbsParts); _navigateTo(url); } catch (e) { NotificationManager.show('alert_error_applying_date', {}, 'error', 5000); }}
        };
    })();

    function addGlobalStyles() { /* ... (same as before) ... */ if (typeof window.GSCS_Namespace !== 'undefined' && typeof window.GSCS_Namespace.stylesText === 'string') { const cleanedCSS = window.GSCS_Namespace.stylesText.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1').replace(/\n\s*\n/g, '\n'); GM_addStyle(cleanedCSS); } else { console.error(`${LOG_PREFIX} CRITICAL: CSS styles provider not found.`); if (typeof IDS !== 'undefined' && IDS.SIDEBAR) { GM_addStyle(`#${IDS.SIDEBAR} { border: 3px dashed red !important; padding: 15px !important; background: white !important; color: red !important; } #${IDS.SIDEBAR}::before { content: "Error: CSS Missing!"; }`);} } }
    function setupSystemThemeListener() { /* ... (same as before) ... */ if (systemThemeMediaQuery && systemThemeMediaQuery._sidebarThemeListener) { try { systemThemeMediaQuery.removeEventListener('change', systemThemeMediaQuery._sidebarThemeListener); } catch (e) {} systemThemeMediaQuery._sidebarThemeListener = null; } if (window.matchMedia) { systemThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const listener = (event) => { const cs = SettingsManager.getCurrentSettings(); if (sidebar && cs.theme === 'system') { applyThemeToElement(sidebar, 'system'); } }; systemThemeMediaQuery.addEventListener('change', listener); systemThemeMediaQuery._sidebarThemeListener = listener; } }
    function buildSidebarSkeleton() { /* ... (same as before) ... */ sidebar = document.createElement('div'); sidebar.id = IDS.SIDEBAR; const header = document.createElement('div'); header.classList.add(CSS.SIDEBAR_HEADER); const collapseBtn = document.createElement('button'); collapseBtn.id = IDS.COLLAPSE_BUTTON; collapseBtn.innerHTML = SVG_ICONS.chevronLeft; collapseBtn.title = _('sidebar_collapse_title'); const dragHandle = document.createElement('div'); dragHandle.classList.add(CSS.DRAG_HANDLE); dragHandle.title = _('sidebar_drag_title'); const settingsBtn = document.createElement('button'); settingsBtn.id = IDS.SETTINGS_BUTTON; settingsBtn.classList.add(CSS.SETTINGS_BUTTON); settingsBtn.innerHTML = SVG_ICONS.settings; settingsBtn.title = _('sidebar_settings_title'); header.appendChild(collapseBtn); header.appendChild(dragHandle); header.appendChild(settingsBtn); sidebar.appendChild(header); document.body.appendChild(sidebar); }
    function applySettings(settingsToApply) { /* ... (same as before) ... */ if (!sidebar) return; const currentSettings = settingsToApply || SettingsManager.getCurrentSettings(); let targetTop = currentSettings.sidebarPosition.top; targetTop = Math.max(MIN_SIDEBAR_TOP_POSITION, targetTop); sidebar.style.left = `${currentSettings.sidebarPosition.left}px`; sidebar.style.top = `${targetTop}px`; sidebar.style.setProperty('--sidebar-font-base-size', `${currentSettings.fontSize}px`); sidebar.style.setProperty('--sidebar-header-icon-base-size', `${currentSettings.headerIconSize}px`); sidebar.style.setProperty('--sidebar-spacing-multiplier', currentSettings.verticalSpacingMultiplier); if (!currentSettings.sidebarCollapsed) { sidebar.style.width = `${currentSettings.sidebarWidth}px`; } else { sidebar.style.width = '40px';} applyThemeToElement(sidebar, currentSettings.theme); if (sidebar._hoverListeners) { sidebar.removeEventListener('mouseenter', sidebar._hoverListeners.enter); sidebar.removeEventListener('mouseleave', sidebar._hoverListeners.leave); sidebar._hoverListeners = null; sidebar.style.opacity = '1';} if (currentSettings.hoverMode && !currentSettings.sidebarCollapsed) { const idleOpacityValue = currentSettings.idleOpacity; sidebar.style.opacity = idleOpacityValue.toString(); const enterL = () => { if (!currentSettings.sidebarCollapsed) sidebar.style.opacity = '1'; }; const leaveL = () => { if (!currentSettings.sidebarCollapsed) sidebar.style.opacity = idleOpacityValue.toString(); }; sidebar.addEventListener('mouseenter', enterL); sidebar.addEventListener('mouseleave', leaveL); sidebar._hoverListeners = { enter: enterL, leave: leaveL }; } else { sidebar.style.opacity = '1'; } applySidebarCollapseVisuals(currentSettings.sidebarCollapsed); }

    // --- UI Building Helper Functions ---
    function _parseTimeValueToMinutes(timeValue) {
        if (!timeValue || typeof timeValue !== 'string') return Infinity;
        const match = timeValue.match(/^([hdwmy])(\d*)$/i);
        if (!match) return Infinity;
        const unit = match[1].toLowerCase();
        const number = parseInt(match[2] || '1', 10);
        if (isNaN(number)) return Infinity;
        switch (unit) {
            case 'h': return number * 60;
            case 'd': return number * 24 * 60;
            case 'w': return number * 7 * 24 * 60;
            case 'm': return number * 30 * 24 * 60; // Approximation
            case 'y': return number * 365 * 24 * 60; // Approximation
            default: return Infinity;
        }
    }

    function _prepareFilterOptions(id, scriptDefinedOptions, enabledPredefinedValues = [], customOptions = [], predefinedOptions = []) {
        const CHINESE_LANG_SORT_ORDER = { 'lang_zh-TW|lang_zh-CN': 0, 'lang_zh-TW': 1, 'lang_zh-CN': 2 };
        const combinedOptions = [];
        const addedValues = new Set();
        const isLanguageSection = (id === 'sidebar-section-language');
        const isTimeSection = (id === 'sidebar-section-time');

        const validCustomOptions = Array.isArray(customOptions) ? customOptions.filter(cOpt => cOpt && typeof cOpt.text === 'string' && typeof cOpt.value === 'string') : [];
        validCustomOptions.forEach(opt => {
            combinedOptions.push({ text: opt.text, value: opt.value, originalText: opt.text, isCustom: true });
            addedValues.add(opt.value);
        });

        scriptDefinedOptions.forEach(opt => {
            if (opt && typeof opt.textKey === 'string' && typeof opt.v === 'string' && !addedValues.has(opt.v)) {
                const translatedText = _(opt.textKey);
                combinedOptions.push({ text: translatedText, value: opt.v, originalText: translatedText });
                addedValues.add(opt.v);
            }
        });

        const enabledSet = new Set(enabledPredefinedValues);
        if (Array.isArray(predefinedOptions)) {
            predefinedOptions.forEach(opt => {
                if (opt && typeof opt.textKey === 'string' && typeof opt.value === 'string' && enabledSet.has(opt.value) && !addedValues.has(opt.value)) {
                    const translatedText = _(opt.textKey);
                    combinedOptions.push({ text: translatedText, value: opt.value, originalText: translatedText });
                }
            });
        }

        combinedOptions.sort((a, b) => {
            if (a.value === '' && b.value !== '') return -1;
            if (a.value !== '' && b.value === '') return 1;
            if (a.value === '' && b.value === '') return 0;

            if (isTimeSection) {
                const timeA = _parseTimeValueToMinutes(a.value);
                const timeB = _parseTimeValueToMinutes(b.value);
                if (timeA !== timeB) {
                    return timeA - timeB;
                }
                return (a.originalText || a.text).localeCompare(b.originalText || b.text, LocalizationService.getCurrentLocale() === 'en' ? undefined : LocalizationService.getCurrentLocale(), { numeric: true, sensitivity: 'base' });
            } else if (isLanguageSection) {
                const aS = CHINESE_LANG_SORT_ORDER.hasOwnProperty(a.value);
                const bS = CHINESE_LANG_SORT_ORDER.hasOwnProperty(b.value);
                if (aS && bS) return CHINESE_LANG_SORT_ORDER[a.value] - CHINESE_LANG_SORT_ORDER[b.value];
                else if (aS) return -1;
                else if (bS) return 1;
            }
            const sTA = a.text;
            const sTB = b.text;
            const sL = LocalizationService.getCurrentLocale() === 'en' ? undefined : LocalizationService.getCurrentLocale();
            return sTA.localeCompare(sTB, sL, { numeric: true, sensitivity: 'base' });
        });
        return combinedOptions;
    }

    function _createFilterOptionElement(optionData, filterParam, isCountrySection, countryDisplayMode) {
        const optionElement = document.createElement('div');
        optionElement.classList.add(CSS.FILTER_OPTION);
        const displayText = optionData.text;
        if (isCountrySection) {
            const { icon, text: countryTextOnly } = Utils.parseIconAndText(displayText);
            switch (countryDisplayMode) {
                case 'textOnly': optionElement.textContent = countryTextOnly || displayText; break;
                case 'iconOnly': if (icon) { optionElement.innerHTML = `<span class="country-icon-container">${icon}</span>`; } else { optionElement.textContent = countryTextOnly || displayText; } break;
                case 'iconAndText': default: if (icon) { const textPart = countryTextOnly || displayText.substring(icon.length).trim(); optionElement.innerHTML = `<span class="country-icon-container">${icon}</span>${textPart}`; } else { optionElement.textContent = displayText; } break;
            }
        } else {
            optionElement.textContent = displayText;
        }
        optionElement.title = `${displayText} (${filterParam}=${optionData.value || _('filter_clear_tooltip_suffix')})`;
        optionElement.dataset[DATA_ATTR.FILTER_TYPE] = filterParam;
        optionElement.dataset[DATA_ATTR.FILTER_VALUE] = optionData.value;
        return optionElement;
    }

    function buildSidebarUI() {
        if (!sidebar) { console.error("Sidebar element not ready for buildSidebarUI"); return; }
        const currentSettings = SettingsManager.getCurrentSettings();
        const header = sidebar.querySelector(`.${CSS.SIDEBAR_HEADER}`);
        if (!header) { console.error("Sidebar header not found in buildSidebarUI"); return; }

        sidebar.querySelectorAll(`#${IDS.FIXED_TOP_BUTTONS}, .${CSS.SIDEBAR_CONTENT_WRAPPER}`).forEach(el => el.remove());
        header.querySelectorAll(`.${CSS.HEADER_BUTTON}:not(#${IDS.SETTINGS_BUTTON}):not(#${IDS.COLLAPSE_BUTTON}), a.${CSS.HEADER_BUTTON}`).forEach(el => el.remove());

        const rBL = currentSettings.resetButtonLocation;
        const vBL = currentSettings.verbatimButtonLocation;
        const aSL = currentSettings.advancedSearchLinkLocation;
        const pznBL = currentSettings.personalizationButtonLocation;
        const sBR = header.querySelector(`#${IDS.SETTINGS_BUTTON}`);

        _buildSidebarHeaderControls(header, sBR, rBL, vBL, aSL, pznBL, _createAdvancedSearchElementHTML, _createPersonalizationButtonHTML, currentSettings);
        const fTC = _buildSidebarFixedTopControls(rBL, vBL, aSL, pznBL, _createAdvancedSearchElementHTML, _createPersonalizationButtonHTML, currentSettings);
        if (fTC) header.after(fTC);

        const cW = document.createElement('div'); cW.classList.add(CSS.SIDEBAR_CONTENT_WRAPPER);
        const sDM = new Map(ALL_SECTION_DEFINITIONS.map(def => [def.id, def]));
        const sF = _buildSidebarSections(sDM, rBL, vBL, aSL, pznBL, _createAdvancedSearchElementHTML, _createPersonalizationButtonHTML, currentSettings, PREDEFINED_OPTIONS);
        cW.appendChild(sF); sidebar.appendChild(cW);
        _initializeSidebarEventListenersAndStates();
    }

    function _buildSidebarSections(sectionDefinitionMap, rBL, vBL, aSL, pznBL, createAdvancedSearchElementFn, createPersonalizationButtonFn, currentSettings, PREDEFINED_OPTIONS_REF) {
        const contentFragment = document.createDocumentFragment();
        currentSettings.sidebarSectionOrder.forEach(sectionId => {
            if (!currentSettings.visibleSections[sectionId]) return;
            const sectionData = sectionDefinitionMap.get(sectionId);
            if (!sectionData) { console.warn(`${LOG_PREFIX} No definition for section ID: ${sectionId}`); return; }
            let sectionElement = null;
            const sectionTitleKey = sectionData.titleKey; const sectionIdForDisplay = sectionData.id;
            switch (sectionData.type) {
                case 'filter': sectionElement = createFilterSection(sectionIdForDisplay, sectionTitleKey, sectionData.scriptDefined, sectionData.param, currentSettings.enabledPredefinedOptions[sectionData.predefinedOptionsKey] || [], currentSettings[sectionData.customItemsKey] || [], PREDEFINED_OPTIONS_REF[sectionData.predefinedOptionsKey] || [], currentSettings.countryDisplayMode); break;
                case 'date': sectionElement = _createDateSectionElement(sectionIdForDisplay, sectionTitleKey); break;
                case 'site': sectionElement = _createSiteSearchSectionElement(sectionIdForDisplay, sectionTitleKey, currentSettings.favoriteSites); break;
                case 'tools':
                    sectionElement = _createToolsSectionElement( sectionIdForDisplay, sectionTitleKey, rBL, vBL, aSL, pznBL, createAdvancedSearchElementFn, createPersonalizationButtonFn );
                    break;
                default: console.warn(`${LOG_PREFIX} Unknown section type: ${sectionData.type} for ID: ${sectionIdForDisplay}`); break;
            }
            if (sectionElement) contentFragment.appendChild(sectionElement);
        });
        return contentFragment;
    }

    function createFilterSection(id, titleKey, scriptDefinedOptions, filterParam, enabledPredefinedValues = [], customOptions = [], predefinedOptions = [], countryDisplayMode) {
        if (!sidebar) return null;
        const { section, sectionContent, sectionTitle } = _createSectionShell(id, titleKey);
        sectionTitle.textContent = _(titleKey);
        const fragment = document.createDocumentFragment();
        const isCountrySection = (id === 'sidebar-section-country');
        const combinedOptions = _prepareFilterOptions(id, scriptDefinedOptions, enabledPredefinedValues, customOptions, predefinedOptions);
        combinedOptions.forEach(option => {
            fragment.appendChild(_createFilterOptionElement(option, filterParam, isCountrySection, countryDisplayMode));
        });
        sectionContent.innerHTML = ''; sectionContent.appendChild(fragment);
        const oldListener = sectionContent._filterClickListener; if (oldListener) sectionContent.removeEventListener('click', oldListener);
        const newListener = function(event) {
            const target = event.target.closest(`.${CSS.FILTER_OPTION}`);
            if (target && target.classList.contains(CSS.FILTER_OPTION)) {
                event.preventDefault();
                const clickedFilterType = target.dataset[DATA_ATTR.FILTER_TYPE];
                const clickedFilterValue = target.dataset[DATA_ATTR.FILTER_VALUE];
                if (typeof clickedFilterType !== 'undefined' && typeof clickedFilterValue !== 'undefined') {
                    this.querySelectorAll(`.${CSS.FILTER_OPTION}`).forEach(opt => opt.classList.remove(CSS.SELECTED));
                    target.classList.add(CSS.SELECTED);
                    if (clickedFilterValue === '') { const anyOpt = this.querySelector(`.${CSS.FILTER_OPTION}[data-${DATA_ATTR.FILTER_VALUE}=""]`); if (anyOpt) anyOpt.classList.add(CSS.SELECTED); }
                    URLActionManager.applyFilter(clickedFilterType, clickedFilterValue);
                }
            }
        };
        sectionContent.addEventListener('click', newListener); sectionContent._filterClickListener = newListener;
        return section;
    }
    function _createSiteSearchSectionElement(sectionId, titleKey, favoriteSites) { const { section, sectionContent, sectionTitle } = _createSectionShell(sectionId, titleKey); sectionTitle.textContent = _(titleKey); const list = document.createElement('ul'); list.classList.add(CSS.CUSTOM_LIST); sectionContent.appendChild(list); populateSiteSearchList(list, favoriteSites); return section; }
    function populateSiteSearchList(listElement, favoriteSitesArray) { if (!listElement) { console.error("Site search list element missing in populateSiteSearchList"); return; } listElement.innerHTML = ''; const sites = Array.isArray(favoriteSitesArray) ? favoriteSitesArray : []; const fragment = document.createDocumentFragment(); sites.forEach((site, index) => { if (site?.text && site?.url) { const li = document.createElement('li'); const opt = document.createElement('div'); opt.classList.add(CSS.FILTER_OPTION); opt.dataset[DATA_ATTR.SITE_URL] = site.url; opt.title = _('tooltip_site_search', { siteUrl: site.url }); opt.textContent = site.text; li.appendChild(opt); fragment.appendChild(li); }}); const clearLi = document.createElement('li'); const clearOpt = document.createElement('div'); clearOpt.classList.add(CSS.FILTER_OPTION); clearOpt.id = 'clear-site-search-option'; clearOpt.title = _('tooltip_clear_site_search'); clearOpt.textContent = _('filter_clear_site_search'); clearLi.appendChild(clearOpt); fragment.appendChild(clearLi); listElement.appendChild(fragment); if (!listElement.dataset[DATA_ATTR.LISTENER_ATTACHED]) { listElement.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true'; listElement.addEventListener('click', (event) => { const target = event.target.closest(`.${CSS.FILTER_OPTION}`); if (target) { listElement.querySelectorAll(`.${CSS.FILTER_OPTION}.${CSS.SELECTED}`).forEach(opt => opt.classList.remove(CSS.SELECTED)); if (target.id === 'clear-site-search-option') { URLActionManager.clearSiteSearch(); } else { const siteUrl = target.dataset[DATA_ATTR.SITE_URL]; if (siteUrl) { URLActionManager.applySiteSearch(siteUrl); target.classList.add(CSS.SELECTED); } } } }); } }
    function renderSectionOrderList(settingsRef) { const settingsWindowEl = document.getElementById(IDS.SETTINGS_WINDOW); const orderListElement = settingsWindowEl?.querySelector(`#${IDS.SIDEBAR_SECTION_ORDER_LIST}`); if (!orderListElement) { return; } orderListElement.innerHTML = ''; const currentSettings = settingsRef || SettingsManager.getCurrentSettings(); const visibleOrderedSections = currentSettings.sidebarSectionOrder.filter(id => currentSettings.visibleSections[id]); if (visibleOrderedSections.length === 0) { orderListElement.innerHTML = `<li><span style="font-style:italic;color:var(--settings-tab-color);">${_('settings_no_orderable_sections')}</span></li>`; return; } const fragment = document.createDocumentFragment(); visibleOrderedSections.forEach((sectionId, index) => { const definition = ALL_SECTION_DEFINITIONS.find(def => def.id === sectionId); const displayName = definition ? _(definition.titleKey) : sectionId; const listItem = document.createElement('li'); listItem.dataset.sectionId = sectionId; const nameSpan = document.createElement('span'); nameSpan.textContent = displayName; listItem.appendChild(nameSpan); const buttonsDiv = document.createElement('div'); buttonsDiv.className = 'order-buttons'; const upButton = document.createElement('button'); upButton.innerHTML = '&uarr;'; upButton.title = _('settings_move_up_title'); upButton.classList.add('move-section-up'); upButton.disabled = (index === 0); buttonsDiv.appendChild(upButton); const downButton = document.createElement('button'); downButton.innerHTML = '&darr;'; downButton.title = _('settings_move_down_title'); downButton.classList.add('move-section-down'); downButton.disabled = (index === visibleOrderedSections.length - 1); buttonsDiv.appendChild(downButton); listItem.appendChild(buttonsDiv); fragment.appendChild(listItem); }); orderListElement.appendChild(fragment); }
    function _initMenuCommands() { if (typeof GM_registerMenuCommand === 'function') { const openSettingsText = _('menu_open_settings'); const resetAllText = _('menu_reset_all_settings'); if (typeof GM_unregisterMenuCommand === 'function') { try { GM_unregisterMenuCommand(openSettingsText); GM_unregisterMenuCommand(resetAllText); } catch (e) {} } GM_registerMenuCommand(openSettingsText, SettingsManager.show.bind(SettingsManager)); GM_registerMenuCommand(resetAllText, SettingsManager.resetAllFromMenu.bind(SettingsManager)); } }
    function _createSectionShell(id, titleKey) { const section = document.createElement('div'); section.id = id; section.classList.add(CSS.SIDEBAR_SECTION); const sectionTitle = document.createElement('div'); sectionTitle.classList.add(CSS.SECTION_TITLE); sectionTitle.textContent = _(titleKey); section.appendChild(sectionTitle); const sectionContent = document.createElement('div'); sectionContent.classList.add(CSS.SECTION_CONTENT); section.appendChild(sectionContent); return { section, sectionContent, sectionTitle }; }

    function _createDateSectionElement(sectionId, titleKey) {
        const { section, sectionContent, sectionTitle } = _createSectionShell(sectionId, titleKey);
        sectionTitle.textContent = _(titleKey);

        const today = new Date();
        const yyyy = today.getFullYear();
        const mm = String(today.getMonth() + 1).padStart(2, '0');
        const dd = String(today.getDate()).padStart(2, '0');
        const todayString = `${yyyy}-${mm}-${dd}`;

        sectionContent.innerHTML =
            `<label class="${CSS.DATE_INPUT_LABEL}" for="${IDS.DATE_MIN}">${_('date_range_from')}</label>` +
            `<input type="date" class="${CSS.DATE_INPUT}" id="${IDS.DATE_MIN}" max="${todayString}">` +
            `<label class="${CSS.DATE_INPUT_LABEL}" for="${IDS.DATE_MAX}">${_('date_range_to')}</label>` +
            `<input type="date" class="${CSS.DATE_INPUT}" id="${IDS.DATE_MAX}" max="${todayString}">` +
            `<span id="${IDS.DATE_RANGE_ERROR_MSG}" class="${CSS.DATE_RANGE_ERROR_MSG} ${CSS.INPUT_ERROR_MESSAGE}"></span>` +
            `<button class="${CSS.TOOL_BUTTON} apply-date-range">${_('tool_apply_date')}</button>`;
        return section;
    }

    function _createStandardButton({ id = null, className, svgIcon, textContent = null, title, clickHandler, isActive = false }) {
        const button = document.createElement('button');
        if (id) button.id = id;
        button.classList.add(className);
        if (isActive) button.classList.add(CSS.ACTIVE);
        button.title = title;
        let content = svgIcon || '';
        if (textContent) { content = svgIcon ? `${svgIcon} ${textContent}` : textContent; }
        button.innerHTML = content.trim();
        if (clickHandler) {
            if (!button.dataset[DATA_ATTR.LISTENER_ATTACHED]) {
                const wrappedClickHandler = function(event) {
                    clickHandler(event);
                };
                button.addEventListener('click', wrappedClickHandler);
                button.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true';
            }
        }
        return button;
    }

    function _createPersonalizationButtonHTML(forLocation = 'tools') {
        const personalizationActive = URLActionManager.isPersonalizationActive();
        const isIconOnlyLocation = (forLocation === 'header');
        const svgIcon = SVG_ICONS.personalization || '';
        const displayText = !isIconOnlyLocation ? _('tool_personalization_toggle') : '';
        const titleKey = personalizationActive ? 'tooltip_toggle_personalization_off' : 'tooltip_toggle_personalization_on';

        return _createStandardButton({
            id: IDS.TOOL_PERSONALIZE,
            className: (forLocation === 'header') ? CSS.HEADER_BUTTON : CSS.TOOL_BUTTON,
            svgIcon: svgIcon,
            textContent: displayText,
            title: _(titleKey),
            clickHandler: () => URLActionManager.triggerTogglePersonalization(),
            isActive: personalizationActive
        });
    }

    function _createAdvancedSearchElementHTML(isButtonLike = false) {
        const el = document.createElement('a');
        let iconHTML = SVG_ICONS.magnifyingGlass || '';
        if (isButtonLike) {
            el.classList.add(CSS.TOOL_BUTTON);
            el.innerHTML = `${iconHTML} ${_('tool_advanced_search')}`;
        } else {
            el.classList.add(CSS.HEADER_BUTTON);
            el.innerHTML = iconHTML;
        }
        const baseUrl = "https://www.google.com/advanced_search";
        let finalUrl = baseUrl;
        try {
            const currentFullUrl = Utils.getCurrentURL();
            if (currentFullUrl) {
                const currentQuery = currentFullUrl.searchParams.get('q');
                if (currentQuery) {
                    const queryWithoutSite = currentQuery.replace(/\s*site:[\w.-]+\s*/gi, ' ').trim();
                    if (queryWithoutSite) {
                         finalUrl = `${baseUrl}?as_q=${encodeURIComponent(queryWithoutSite)}`;
                    }
                }
            }
        } catch (e) {
            console.warn(`${LOG_PREFIX} Error constructing advanced search URL with query:`, e);
        }
        el.href = finalUrl;
        el.target = "_blank";
        el.rel = "noopener noreferrer";
        el.title = _('link_advanced_search_title');
        return el;
    }

    function _buildSidebarHeaderControls(headerEl, settingsBtnRef, rBL, vBL, aSL, pznBL, advSearchFn, personalizeBtnFn, settings) {
        const verbatimActive = URLActionManager.isVerbatimActive();
        const buttonsInOrder = [];

        if (aSL === 'header' && advSearchFn && settings.advancedSearchLinkLocation !== 'none') {
            buttonsInOrder.push(advSearchFn(false));
        }
        if (vBL === 'header' && settings.verbatimButtonLocation !== 'none') {
            buttonsInOrder.push(_createStandardButton({ id: IDS.TOOL_VERBATIM, className: CSS.HEADER_BUTTON, svgIcon: SVG_ICONS.verbatim, title: _('tool_verbatim_search'), clickHandler: URLActionManager.triggerToggleVerbatim, isActive: verbatimActive }));
        }
        if (pznBL === 'header' && personalizeBtnFn && settings.personalizationButtonLocation !== 'none') {
            buttonsInOrder.push(personalizeBtnFn('header'));
        }
        if (rBL === 'header' && settings.resetButtonLocation !== 'none') {
            buttonsInOrder.push(_createStandardButton({ id: IDS.TOOL_RESET_BUTTON, className: CSS.HEADER_BUTTON, svgIcon: SVG_ICONS.reset, title: _('tool_reset_filters'), clickHandler: URLActionManager.triggerResetFilters }));
        }

        buttonsInOrder.forEach(btn => {
            if (settingsBtnRef) {
                headerEl.insertBefore(btn, settingsBtnRef);
            } else {
                headerEl.appendChild(btn);
            }
        });
    }

    function _buildSidebarFixedTopControls(rBL, vBL, aSL, pznBL, advSearchFn, personalizeBtnFn, settings) {
        const fTBC = document.createElement('div');
        fTBC.id = IDS.FIXED_TOP_BUTTONS;
        const fTF = document.createDocumentFragment();
        const verbatimActive = URLActionManager.isVerbatimActive();

        if (rBL === 'topBlock' && settings.resetButtonLocation !== 'none') {
            const btn = _createStandardButton({ id: IDS.TOOL_RESET_BUTTON, className: CSS.TOOL_BUTTON, svgIcon: SVG_ICONS.reset, textContent: _('tool_reset_filters'), title: _('tool_reset_filters'), clickHandler: URLActionManager.triggerResetFilters });
            const bD = document.createElement('div'); bD.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bD.appendChild(btn);
            fTF.appendChild(bD);
        }
        if (pznBL === 'topBlock' && personalizeBtnFn && settings.personalizationButtonLocation !== 'none') {
            const btnPzn = personalizeBtnFn('topBlock');
            const bDPzn = document.createElement('div'); bDPzn.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bDPzn.appendChild(btnPzn);
            fTF.appendChild(bDPzn);
        }
        if (vBL === 'topBlock' && settings.verbatimButtonLocation !== 'none') {
            const btnVerbatim = _createStandardButton({ id: IDS.TOOL_VERBATIM, className: CSS.TOOL_BUTTON, svgIcon: SVG_ICONS.verbatim, textContent: _('tool_verbatim_search'), title: _('tool_verbatim_search'), clickHandler: URLActionManager.triggerToggleVerbatim, isActive: verbatimActive });
            const bDVerbatim = document.createElement('div'); bDVerbatim.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bDVerbatim.appendChild(btnVerbatim);
            fTF.appendChild(bDVerbatim);
        }
        if (aSL === 'topBlock' && advSearchFn && settings.advancedSearchLinkLocation !== 'none') {
            const linkEl = advSearchFn(true);
            const bDAdv = document.createElement('div'); bDAdv.classList.add(CSS.FIXED_TOP_BUTTON_ITEM); bDAdv.appendChild(linkEl);
            fTF.appendChild(bDAdv);
        }

        if (fTF.childElementCount > 0) { fTBC.appendChild(fTF); return fTBC; }
        return null;
    }

    function _createToolsSectionElement(sectionId, titleKey, rBL, vBL, aSL, pznBL, advSearchFn, personalizeBtnFn) {
        const { section, sectionContent, sectionTitle } = _createSectionShell(sectionId, titleKey);
        sectionTitle.textContent = _(titleKey);
        const frag = document.createDocumentFragment();
        const verbatimActive = URLActionManager.isVerbatimActive();
        const currentSettings = SettingsManager.getCurrentSettings();

        if (rBL === 'tools' && currentSettings.resetButtonLocation !== 'none') {
            const btn = _createStandardButton({ id: IDS.TOOL_RESET_BUTTON, className: CSS.TOOL_BUTTON, svgIcon: SVG_ICONS.reset, textContent: _('tool_reset_filters'), title: _('tool_reset_filters'), clickHandler: URLActionManager.triggerResetFilters });
            frag.appendChild(btn);
        }
        if (pznBL === 'tools' && personalizeBtnFn && currentSettings.personalizationButtonLocation !== 'none') {
            const btnPzn = personalizeBtnFn('tools');
            frag.appendChild(btnPzn);
        }
        if (vBL === 'tools' && currentSettings.verbatimButtonLocation !== 'none') {
            const btnVerbatim = _createStandardButton({ id: IDS.TOOL_VERBATIM, className: CSS.TOOL_BUTTON, svgIcon: SVG_ICONS.verbatim, textContent: _('tool_verbatim_search'), title: _('tool_verbatim_search'), clickHandler: URLActionManager.triggerToggleVerbatim, isActive: verbatimActive });
            frag.appendChild(btnVerbatim);
        }
        if (aSL === 'tools' && advSearchFn && currentSettings.advancedSearchLinkLocation !== 'none') {
            frag.appendChild(advSearchFn(true));
        }

        if (frag.childElementCount > 0) {
            sectionContent.appendChild(frag);
            return section;
        }
        return null;
    }

    function _validateDateInputs(minInput, maxInput, errorMsgElement) {
        _clearElementMessage(errorMsgElement, CSS.ERROR_VISIBLE);
        minInput.classList.remove(CSS.INPUT_HAS_ERROR);
        maxInput.classList.remove(CSS.INPUT_HAS_ERROR);
        let isValid = true;
        const today = new Date();
        today.setHours(0, 0, 0, 0);

        const startDateStr = minInput.value;
        const endDateStr = maxInput.value;
        let startDate = null;
        let endDate = null;

        if (startDateStr) {
            startDate = new Date(startDateStr);
            startDate.setHours(0,0,0,0);
            if (startDate > today) {
                _showElementMessage(errorMsgElement, 'alert_start_in_future', {}, CSS.ERROR_VISIBLE);
                minInput.classList.add(CSS.INPUT_HAS_ERROR);
                isValid = false;
            }
        }

        if (endDateStr) {
            endDate = new Date(endDateStr);
            endDate.setHours(0,0,0,0);
            // Note: max attribute on input type=date already prevents selecting future dates from calendar.
            // This JS validation catches manual input or programmatic changes.
            // If we strictly rely on 'max' attribute, this specific check 'endDate > today' might be redundant
            // for calendar selection, but good for manual input.
            if (endDate > today && !maxInput.getAttribute('max')) { // Only show error if max not set by attribute which should prevent this
                if (isValid) _showElementMessage(errorMsgElement, 'alert_end_in_future', {}, CSS.ERROR_VISIBLE);
                else errorMsgElement.textContent += " " + _('alert_end_in_future');
                maxInput.classList.add(CSS.INPUT_HAS_ERROR);
                isValid = false;
            }
        }

        if (startDate && endDate && startDate > endDate) {
            if (isValid) _showElementMessage(errorMsgElement, 'alert_end_before_start', {}, CSS.ERROR_VISIBLE);
            else errorMsgElement.textContent += " " + _('alert_end_before_start');
            minInput.classList.add(CSS.INPUT_HAS_ERROR);
            maxInput.classList.add(CSS.INPUT_HAS_ERROR);
            isValid = false;
        }
        return isValid;
    }

    function addDateRangeListener() {
        const dateRangeSection = sidebar?.querySelector('#sidebar-section-date-range');
        if (!dateRangeSection) return;

        const applyButton = dateRangeSection.querySelector('.apply-date-range');
        const errorMsgElement = dateRangeSection.querySelector(`#${IDS.DATE_RANGE_ERROR_MSG}`);
        const dateMinInput = dateRangeSection.querySelector(`#${IDS.DATE_MIN}`);
        const dateMaxInput = dateRangeSection.querySelector(`#${IDS.DATE_MAX}`);

        if (!applyButton || !errorMsgElement || !dateMinInput || !dateMaxInput) {
            console.warn(`${LOG_PREFIX} Date range elements not found for listener setup.`);
            return;
        }

        const handleDateValidation = () => {
            const isValid = _validateDateInputs(dateMinInput, dateMaxInput, errorMsgElement);
            applyButton.disabled = !isValid;
        };

        if (!dateMinInput.dataset[DATA_ATTR.LISTENER_ATTACHED]) {
            dateMinInput.addEventListener('input', handleDateValidation);
            dateMinInput.addEventListener('change', handleDateValidation); // Also on change for calendar
            dateMinInput.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true';
        }
        if (!dateMaxInput.dataset[DATA_ATTR.LISTENER_ATTACHED]) {
            dateMaxInput.addEventListener('input', handleDateValidation);
            dateMaxInput.addEventListener('change', handleDateValidation); // Also on change for calendar
            dateMaxInput.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true';
        }

        if (!applyButton.dataset[DATA_ATTR.LISTENER_ATTACHED]) {
            applyButton.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true';
            applyButton.addEventListener('click', () => {
                if (!_validateDateInputs(dateMinInput, dateMaxInput, errorMsgElement)) {
                    // NotificationManager.show('alert_select_date', {}, 'warning', 3000); // Error already shown by _validateDateInputs
                    return;
                }
                URLActionManager.applyDateRange(dateMinInput.value, dateMaxInput.value);
            });
        }
        handleDateValidation(); // Initial check
    }

    function _initializeSidebarEventListenersAndStates() { addDateRangeListener(); addToolButtonListeners(); initializeSelectedFilters(); applySectionCollapseStates(); }
    function _clearElementMessage(element, visibleClass = CSS.ERROR_VISIBLE) { if(!element)return; element.textContent=''; element.classList.remove(visibleClass);}
    function _showElementMessage(element, messageKey, messageArgs = {}, visibleClass = CSS.ERROR_VISIBLE) { if(!element)return; element.textContent=_(messageKey,messageArgs); element.classList.add(visibleClass);}
    function addToolButtonListeners() { const queryAreas = [ sidebar?.querySelector(`.${CSS.SIDEBAR_HEADER}`), sidebar?.querySelector(`#${IDS.FIXED_TOP_BUTTONS}`), sidebar?.querySelector(`#sidebar-section-tools .${CSS.SECTION_CONTENT}`) ].filter(Boolean); queryAreas.forEach(area => { area.querySelectorAll(`#${IDS.TOOL_VERBATIM}:not([data-${DATA_ATTR.LISTENER_ATTACHED}])`).forEach(b => { b.addEventListener('click', URLActionManager.triggerToggleVerbatim); b.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true'; }); area.querySelectorAll(`#${IDS.TOOL_RESET_BUTTON}:not([data-${DATA_ATTR.LISTENER_ATTACHED}])`).forEach(b => { b.addEventListener('click', URLActionManager.triggerResetFilters); b.dataset[DATA_ATTR.LISTENER_ATTACHED] = 'true'; }); }); }
    function applySidebarCollapseVisuals(isCollapsed) { if(!sidebar)return;const cB=sidebar.querySelector(`#${IDS.COLLAPSE_BUTTON}`);if(isCollapsed){sidebar.classList.add(CSS.SIDEBAR_COLLAPSED);if(cB){cB.innerHTML=SVG_ICONS.chevronRight;cB.title=_('sidebar_expand_title');}}else{sidebar.classList.remove(CSS.SIDEBAR_COLLAPSED);if(cB){cB.innerHTML=SVG_ICONS.chevronLeft;cB.title=_('sidebar_collapse_title');}}}
    function applySectionCollapseStates() { if(!sidebar)return; const currentSettings = SettingsManager.getCurrentSettings(); const sections = sidebar.querySelectorAll(`.${CSS.SIDEBAR_CONTENT_WRAPPER} .${CSS.SIDEBAR_SECTION}`); sections.forEach(section => { const content = section.querySelector(`.${CSS.SECTION_CONTENT}`); const title = section.querySelector(`.${CSS.SECTION_TITLE}`); const sectionId = section.id; if (content && title && sectionId) { let shouldBeCollapsed = false; if (currentSettings.sectionDisplayMode === 'collapseAll') { shouldBeCollapsed = true; } else if (currentSettings.sectionDisplayMode === 'expandAll') { shouldBeCollapsed = false; } else { shouldBeCollapsed = currentSettings.sectionStates?.[sectionId] === true; } content.classList.toggle(CSS.COLLAPSED, shouldBeCollapsed); title.classList.toggle(CSS.COLLAPSED, shouldBeCollapsed); if (currentSettings.sectionDisplayMode === 'remember') { if (!currentSettings.sectionStates) currentSettings.sectionStates = {}; currentSettings.sectionStates[sectionId] = shouldBeCollapsed; } } }); }

    function initializeSelectedFilters() {
        if (!sidebar) return;
        try {
            const u = URLActionManager._getURLObject ? URLActionManager._getURLObject() : Utils.getCurrentURL();
            if (!u) return;
            const p = u.searchParams;
            const cT = p.get('tbs') || '';
            const cQ = p.get('q') || '';
            _initializeStandaloneFilterState(p, 'language', 'lr');
            _initializeStandaloneFilterState(p, 'country', 'cr');
            _initializeStandaloneFilterState(p, 'filetype', 'as_filetype');
            _initializeTimeFilterState(cT);
            _initializeVerbatimState();
            _initializePersonalizationState();
            _initializeDateRangeInputs(cT);
            _initializeSiteSearchState(cQ);
        } catch (e) {
            console.error(`${LOG_PREFIX} Error initializing filter highlights:`, e);
        }
    }
    function _initializeStandaloneFilterState(p,fT,pTC){ const sI=`sidebar-section-${fT}`;const s=sidebar?.querySelector(`#${sI}`);if(!s){return;}const uV=p.get(pTC);const o=s.querySelectorAll(`.${CSS.FILTER_OPTION}`);o.forEach(opt=>{const oV=opt.dataset[DATA_ATTR.FILTER_VALUE];opt.classList.toggle(CSS.SELECTED,(uV!==null&&uV===oV)||(uV===null&&oV===''));});}
    function _initializeTimeFilterState(cT){ const tS=sidebar?.querySelector('#sidebar-section-time');if(!tS)return;const qM=cT.match(/qdr:([^,]+)/);const aQV=qM?qM[1]:null;const hDR=/cdr:1/.test(cT);const tO=tS.querySelectorAll(`.${CSS.FILTER_OPTION}`);tO.forEach(o=>{const oV=o.dataset[DATA_ATTR.FILTER_VALUE];let sS=false;if(hDR){sS=(oV==='');}else if(aQV){sS=(oV===aQV);}else{sS=(oV==='');}o.classList.toggle(CSS.SELECTED,sS);});}
    function _initializeVerbatimState(){ const iVA = URLActionManager.isVerbatimActive(); sidebar?.querySelectorAll(`#${IDS.TOOL_VERBATIM}`).forEach(b=>b.classList.toggle(CSS.ACTIVE, iVA));}
    function _initializePersonalizationState() {
        const isActive = URLActionManager.isPersonalizationActive();
        sidebar?.querySelectorAll(`#${IDS.TOOL_PERSONALIZE}`).forEach(button => {
            button.classList.toggle(CSS.ACTIVE, isActive);
            const titleKey = isActive ? 'tooltip_toggle_personalization_off' : 'tooltip_toggle_personalization_on';
            button.title = _(titleKey);
            // Text and icon are primarily set by _createPersonalizationButtonHTML.
            // This ensures they are correct if the button was somehow not fully reconstructed.
            const svgIcon = SVG_ICONS.personalization || '';
            const isIconOnly = button.classList.contains(CSS.HEADER_BUTTON) && !button.classList.contains(CSS.TOOL_BUTTON);
            const currentText = !isIconOnly ? _('tool_personalization_toggle') : '';
            let newHTML = '';
            if (svgIcon) newHTML += svgIcon;
            if (currentText) newHTML += (svgIcon && currentText ? ' ' : '') + currentText;
            button.innerHTML = newHTML.trim();
        });
    }
    function _initializeDateRangeInputs(cT){
        const dS = sidebar?.querySelector('#sidebar-section-date-range');
        if (!dS) return;
        const dMI = dS.querySelector(`#${IDS.DATE_MIN}`);
        const dMXI = dS.querySelector(`#${IDS.DATE_MAX}`);
        const eME = dS.querySelector(`#${IDS.DATE_RANGE_ERROR_MSG}`);
        const applyButton = dS.querySelector('.apply-date-range');

        if (eME) _clearElementMessage(eME, CSS.ERROR_VISIBLE);

        if (/cdr:1/.test(cT)) {
            const mIM = cT.match(/cd_min:(\d{1,2})\/(\d{1,2})\/(\d{4})/);
            const mAM = cT.match(/cd_max:(\d{1,2})\/(\d{1,2})\/(\d{4})/);
            if (dMI) dMI.value = mIM ? `${mIM[3]}-${mIM[1].padStart(2, '0')}-${mIM[2].padStart(2, '0')}` : '';
            if (dMXI) dMXI.value = mAM ? `${mAM[3]}-${mAM[1].padStart(2, '0')}-${mAM[2].padStart(2, '0')}` : '';
        } else {
            if (dMI) dMI.value = '';
            if (dMXI) dMXI.value = '';
        }
        // After setting values from URL, trigger validation
        if (dMI && dMXI && eME && applyButton) {
            const isValid = _validateDateInputs(dMI, dMXI, eME);
            applyButton.disabled = !isValid;
        }
    }
    function _initializeSiteSearchState(cQ){ const sS=sidebar?.querySelector('#sidebar-section-site-search');if(!sS)return;const sM=cQ.match(/site:([\w.-]+)/);const aSU=sM?sM[1]:null;const sO=sS.querySelectorAll(`.${CSS.FILTER_OPTION}[data-${DATA_ATTR.SITE_URL}]`);sO.forEach(o=>{o.classList.toggle(CSS.SELECTED,aSU&&o.dataset[DATA_ATTR.SITE_URL]===aSU);});const cO=sS.querySelector('#clear-site-search-option');if(cO){cO.classList.toggle(CSS.SELECTED,!aSU);}}

    function bindSidebarEvents() { /* ... (same as before) ... */ if (!sidebar) return; const collapseButton = sidebar.querySelector(`#${IDS.COLLAPSE_BUTTON}`); const settingsButton = sidebar.querySelector(`#${IDS.SETTINGS_BUTTON}`); if (collapseButton) collapseButton.title = _('sidebar_collapse_title'); if (settingsButton) settingsButton.title = _('sidebar_settings_title'); sidebar.addEventListener('click', (e) => { const settingsBtnTarget = e.target.closest(`#${IDS.SETTINGS_BUTTON}`); if (settingsBtnTarget) { SettingsManager.show(); return; } const collapseBtnTarget = e.target.closest(`#${IDS.COLLAPSE_BUTTON}`); if (collapseBtnTarget) { toggleSidebarCollapse(); return; } const sectionTitleTarget = e.target.closest(`.${CSS.SIDEBAR_CONTENT_WRAPPER} .${CSS.SECTION_TITLE}`); if (sectionTitleTarget && !sidebar.classList.contains(CSS.SIDEBAR_COLLAPSED)) { handleSectionCollapse(e); return; } }); }
    function toggleSidebarCollapse() { /* ... (same as before) ... */ const cs = SettingsManager.getCurrentSettings(); cs.sidebarCollapsed = !cs.sidebarCollapsed; applySettings(cs); SettingsManager.save('Sidebar Collapse');}
    function handleSectionCollapse(event) { /* ... (same as before) ... */ const title = event.target.closest(`.${CSS.SECTION_TITLE}`); if (!title || sidebar?.classList.contains(CSS.SIDEBAR_COLLAPSED) || title.closest(`#${IDS.FIXED_TOP_BUTTONS}`)) return; const section = title.closest(`.${CSS.SIDEBAR_SECTION}`); if (!section) return; const content = section.querySelector(`.${CSS.SECTION_CONTENT}`); const sectionId = section.id; if (!content || !sectionId) return; const currentSettings = SettingsManager.getCurrentSettings(); const isCurrentlyCollapsed = content.classList.contains(CSS.COLLAPSED); const shouldBeCollapsedAfterClick = !isCurrentlyCollapsed; let overallStateChanged = false; if (currentSettings.accordionMode && !shouldBeCollapsedAfterClick) { const sectionsContainer = section.parentElement; if (_applyAccordionEffectToSections(sectionId, sectionsContainer, currentSettings)) overallStateChanged = true; } if (_toggleSectionVisualState(section, title, content, sectionId, shouldBeCollapsedAfterClick, currentSettings)) overallStateChanged = true; if (overallStateChanged && currentSettings.sectionDisplayMode === 'remember') { debouncedSaveSettings('Section Collapse/Accordion'); } }
    function _applyAccordionEffectToSections(clickedSectionId, allSectionsContainer, currentSettings) { /* ... (same as before) ... */ let stateChangedForAccordion = false; allSectionsContainer?.querySelectorAll(`.${CSS.SIDEBAR_SECTION}`)?.forEach(otherSection => { if (otherSection.id !== clickedSectionId) { const otherContent = otherSection.querySelector(`.${CSS.SECTION_CONTENT}`); const otherTitle = otherSection.querySelector(`.${CSS.SECTION_TITLE}`); if (otherContent && !otherContent.classList.contains(CSS.COLLAPSED)) { otherContent.classList.add(CSS.COLLAPSED); otherTitle?.classList.add(CSS.COLLAPSED); if (currentSettings.sectionDisplayMode === 'remember') { if (!currentSettings.sectionStates) currentSettings.sectionStates = {}; if (currentSettings.sectionStates[otherSection.id] !== true) { currentSettings.sectionStates[otherSection.id] = true; stateChangedForAccordion = true; } } } } }); return stateChangedForAccordion; }
    function _toggleSectionVisualState(sectionEl, titleEl, contentEl, sectionId, newCollapsedState, currentSettings) { /* ... (same as before) ... */ let sectionStateActuallyChanged = false; const isCurrentlyCollapsed = contentEl.classList.contains(CSS.COLLAPSED); if (isCurrentlyCollapsed !== newCollapsedState) { contentEl.classList.toggle(CSS.COLLAPSED, newCollapsedState); titleEl.classList.toggle(CSS.COLLAPSED, newCollapsedState); sectionStateActuallyChanged = true; } if (currentSettings.sectionDisplayMode === 'remember') { if (!currentSettings.sectionStates) currentSettings.sectionStates = {}; if (currentSettings.sectionStates[sectionId] !== newCollapsedState) { currentSettings.sectionStates[sectionId] = newCollapsedState; if (!sectionStateActuallyChanged) sectionStateActuallyChanged = true; } } return sectionStateActuallyChanged; }

    // --- Main Initialization ---
    function initializeScript() {
        console.log(LOG_PREFIX + " Initializing script...");
        debouncedSaveSettings = Utils.debounce(() => SettingsManager.save('Debounced Save'), 800);
        try {
            addGlobalStyles();
            NotificationManager.init();
            LocalizationService.initializeBaseLocale();
            SettingsManager.initialize( defaultSettings, applySettings, buildSidebarUI, applySectionCollapseStates, _initMenuCommands, renderSectionOrderList );
            setupSystemThemeListener();
            buildSidebarSkeleton();
            DragManager.init( sidebar, sidebar.querySelector(`.${CSS.DRAG_HANDLE}`), SettingsManager, debouncedSaveSettings );
            const initialSettings = SettingsManager.getCurrentSettings();
            DragManager.setDraggable(initialSettings.draggableHandleEnabled, sidebar, sidebar.querySelector(`.${CSS.DRAG_HANDLE}`));
            applySettings(initialSettings);
            buildSidebarUI(); // This also calls _initializeSidebarEventListenersAndStates
            bindSidebarEvents();
            _initMenuCommands();
            console.log(`${LOG_PREFIX} Script initialization complete. Final effective locale: ${LocalizationService.getCurrentLocale()}`);
        } catch (error) {
            console.error(`${LOG_PREFIX} [initializeScript] CRITICAL ERROR DURING INITIALIZATION:`, error, error.stack);
            const scriptNameForAlert = (typeof _ === 'function' && _('scriptName') && !(_('scriptName').startsWith('[ERR:'))) ? _('scriptName') : SCRIPT_INTERNAL_NAME;
            if (typeof NotificationManager !== 'undefined' && NotificationManager.show) {
                NotificationManager.show('alert_init_fail', { scriptName: scriptNameForAlert, error: error.message }, 'error', 0);
            } else {
                _showGlobalMessage('alert_init_fail', { scriptName: scriptNameForAlert, error: error.message }, 'error', 0);
            }
            if(sidebar && sidebar.remove) sidebar.remove();
            const settingsOverlayEl = document.getElementById(IDS.SETTINGS_OVERLAY);
            if(settingsOverlayEl) settingsOverlayEl.remove();
            ModalManager.hide();
        }
    }

    // --- Script Entry Point ---
    if (document.getElementById(IDS.SIDEBAR)) { console.warn(`${LOG_PREFIX} Sidebar with ID "${IDS.SIDEBAR}" already exists. Skipping initialization.`); return; }
    if (typeof window.GSCS_Namespace === 'undefined' || typeof window.GSCS_Namespace.stylesText !== 'string') { console.warn(`${LOG_PREFIX} Styles provider not yet available. Delaying initialization.`); let attempts = 0; const maxAttempts = 20; const interval = 100; const checkStylesInterval = setInterval(() => { attempts++; if (typeof window.GSCS_Namespace !== 'undefined' && typeof window.GSCS_Namespace.stylesText === 'string') { clearInterval(checkStylesInterval); if (document.readyState === 'complete' || document.readyState === 'interactive' || document.readyState === 'loaded') { initializeScript(); } else { window.addEventListener('DOMContentLoaded', initializeScript, { once: true }); } } else if (attempts >= maxAttempts) { clearInterval(checkStylesInterval); console.error(`${LOG_PREFIX} Styles provider failed to load. Initializing without external styles.`); if (document.readyState === 'complete' || document.readyState === 'interactive' || document.readyState === 'loaded') { initializeScript(); } else { window.addEventListener('DOMContentLoaded', initializeScript, { once: true }); } } }, interval);
    } else { if (document.readyState === 'complete' || document.readyState === 'interactive' || document.readyState === 'loaded') { initializeScript(); } else { window.addEventListener('DOMContentLoaded', initializeScript, { once: true }); } }

})();