LINUX.SB-Enhance-Script

布局优化与功能增强脚本,包含主题布局、内容过滤、图片灯箱、可配置图床上传与自动签到。

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==UserScript==
// @name         LINUX.SB-Enhance-Script
// @namespace    https://linux.sb/
// @version      1.7.0
// @description  布局优化与功能增强脚本,包含主题布局、内容过滤、图片灯箱、可配置图床上传与自动签到。
// @author       COMCOM
// @match        https://linux.sb/*
// @match        http://linux.sb/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @connect       *
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    var STORAGE_KEY = 'linux-sb-wide-layout-settings-v2';

    var SETTINGS_VERSION = 3;

    var DEFAULTS = {
        version: SETTINGS_VERSION,
        maxWidth: 1100,
        headerHeight: 68,
        fontSize: 15,
        radius: 6,
        sidebarWidth: 270,
        shellPadding: 28,
        columnGap: 28,
        theme: 'neutral',
        accent: '#b8b8b8',
        textPalette: 'neutral',
        textColor: '#eeeeee',
        homePersonalized: false,
        sidebarSwap: false,
        autoCheckin: false,
        autoCheckinLastDate: '',
        imageLightbox: false,
        imageUpload: false,
        imageUploadProvider: 'imgur',
        imageUploadHost: 'https://imgur.com/',
        imageUploadEndpoint: 'https://api.imgur.com/3/image',
        imageUploadFileField: 'image',
        imageUploadResponsePath: 'data.link',
        imageUploadAuthMode: 'imgur-client-id',
        imageUploadToken: '',
        imageUploadSettingsCollapsed: false,
        panelLeft: null,
        panelTop: null,
        toggleLeft: null,
        toggleTop: null,
        titleFilters: [],
        userFilters: []
    };

    var NODEIMAGE_PRESET = {
        provider: 'nodeimage',
        host: 'https://nodeimage.com/',
        endpoint: 'https://api.nodeimage.com/api/upload',
        field: 'image',
        responsePath: 'data.url',
        authMode: 'nodeimage-api-key',
        token: ''
    };

    var TEXT_PALETTES = {
        neutral: { label: '中性银白', color: '#eeeeee' },
        mist: { label: '雾灰', color: '#d6d6d6' },
        silver: { label: '柔银', color: '#c7c7c7' },
        warm: { label: '暖白', color: '#eee9e1' },
        custom: { label: '自定义颜色', color: '#eeeeee' }
    };

    var HOME_LOGO_SVG = [
        '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="LINUX SB logo">',
        '<circle fill="#EFEFEF" cx="256" cy="256" r="256"/>',
        '<path d="M279.69124,269.784594 C289.092146,253.471257 283.562201,232.595717 267.248865,223.194811 C250.935529,213.793906 230.059989,219.32385 220.659083,235.637186 L212.087669,250.429788 C202.686764,266.743124 208.216708,287.618664 224.530044,297.01957 C240.84338,306.420476 261.718921,300.890531 271.119826,284.577195 L279.69124,269.784594 Z" fill="#737373"/>',
        '<path d="M298.6313,68.356366 C308.032206,52.04303 302.502261,31.1674897 286.188925,21.7665841 C269.875589,12.3656785 249.000048,17.895623 239.599143,34.208959 L145.866584,196.651078 C136.465678,212.964414 141.995623,233.839955 158.308959,243.24086 C174.622295,252.641766 195.497836,247.111821 204.898741,230.798485 L298.6313,68.356366 Z" fill="#1E1E20"/>',
        '<path d="M355.419725,307.249829 C364.82523,290.910346 359.29258,270.001346 342.971263,260.585373 C326.649946,251.1694 305.764193,256.708207 296.358689,273.047691 L202.580275,435.750171 C193.17477,452.089654 198.70742,472.998654 215.028737,482.414627 C231.350054,491.8306 252.235807,486.291793 261.641311,469.952309 L355.419725,307.249829 Z" fill="#FEB005"/>',
        '</svg>'
    ].join('');

    var RANGE_DEFINITIONS = [
        { key: 'maxWidth', label: '页面最大宽度', min: 960, max: 2200, step: 20, unit: 'px' },
        { key: 'headerHeight', label: '顶部栏高度', min: 48, max: 100, step: 2, unit: 'px' },
        { key: 'fontSize', label: '正文基准字号', min: 13, max: 22, step: 1, unit: 'px' },
        { key: 'radius', label: '全局圆角', min: 0, max: 14, step: 1, unit: 'px' },
        { key: 'sidebarWidth', label: '侧栏宽度', min: 220, max: 420, step: 10, unit: 'px' },
        { key: 'shellPadding', label: '内容内边距', min: 12, max: 56, step: 2, unit: 'px' },
        { key: 'columnGap', label: '正文与侧栏间距', min: 12, max: 56, step: 2, unit: 'px' }
    ];

    /*
     * 默认中性深灰严格使用中性灰阶作为界面底色。
     * 彩色只用于成功、警告、错误等语义反馈,避免背景偏蓝或偏紫。
     */
    var THEMES = {
        neutral: {
            label: '中性深灰(默认)',
            accent: '#b8b8b8',
            textColor: '#eeeeee',
            vars: {
                '--bg': '#121212',
                '--panel': '#1b1b1b',
                '--line': '#343434',
                '--line-soft': '#272727',
                '--text': '#eeeeee',
                '--text-muted': '#b6b6b6',
                '--text-subtle': '#898989',
                '--text-disabled': '#666666',
                '--success': '#9fbea9',
                '--success-soft': '#223128',
                '--danger': '#e28b8b',
                '--danger-soft': '#3a2424',
                '--warning': '#d8b276',
                '--warning-soft': '#382e20',
                '--info': '#9bb4d0',
                '--info-soft': '#25303b',
                '--inverse': '#090909',
                '--inverse-border': '#3a3a3a',
                '--inverse-text': '#f5f5f5',
                '--color-dark-rgb': '0,0,0',
                '--backdrop': 'rgba(0,0,0,.68)',
                '--shadow-base': 'rgba(0,0,0,.28)',
                '--shadow-medium': 'rgba(0,0,0,.48)'
            }
        },
        graphite: {
            label: '石墨青',
            accent: '#7faea6',
            textColor: '#ecf0f2',
            vars: {
                '--bg': '#111416',
                '--panel': '#191d20',
                '--line': '#30363b',
                '--line-soft': '#24292d',
                '--text': '#ecf0f2',
                '--text-muted': '#a9b1b7',
                '--text-subtle': '#77818a',
                '--text-disabled': '#5f686f',
                '--success': '#8bb59b',
                '--success-soft': '#203329',
                '--danger': '#df8888',
                '--danger-soft': '#382326',
                '--warning': '#d2ab70',
                '--warning-soft': '#362d20',
                '--info': '#8fafd0',
                '--info-soft': '#24303c',
                '--inverse': '#090b0c',
                '--inverse-border': '#343b40',
                '--inverse-text': '#f4f7f8',
                '--color-dark-rgb': '3,5,6',
                '--backdrop': 'rgba(3,5,6,.68)',
                '--shadow-base': 'rgba(0,0,0,.30)',
                '--shadow-medium': 'rgba(0,0,0,.50)'
            }
        },
        warm: {
            label: '暖石墨',
            accent: '#b8a27f',
            textColor: '#eee9e1',
            vars: {
                '--bg': '#151310',
                '--panel': '#1e1b17',
                '--line': '#37322b',
                '--line-soft': '#29251f',
                '--text': '#eee9e1',
                '--text-muted': '#b8afa3',
                '--text-subtle': '#8b8277',
                '--text-disabled': '#685f55',
                '--success': '#a3b68e',
                '--success-soft': '#293022',
                '--danger': '#df8e84',
                '--danger-soft': '#3a2521',
                '--warning': '#d4ad70',
                '--warning-soft': '#392f20',
                '--info': '#99adc2',
                '--info-soft': '#283039',
                '--inverse': '#0b0a08',
                '--inverse-border': '#3b362f',
                '--inverse-text': '#f7f3ec',
                '--color-dark-rgb': '5,4,3',
                '--backdrop': 'rgba(5,4,3,.68)',
                '--shadow-base': 'rgba(0,0,0,.28)',
                '--shadow-medium': 'rgba(0,0,0,.48)'
            }
        },
        original: {
            label: '跟随原站主题',
            accent: '#5eaaa0',
            textColor: '#c8c9d9',
            vars: null
        }
    };

    var THEME_VARIABLES = [
        '--bg', '--panel', '--line', '--line-soft',
        '--text', '--text-muted', '--text-subtle', '--text-disabled',
        '--brand', '--brand-hover', '--brand-soft',
        '--success', '--success-soft', '--danger', '--danger-soft',
        '--warning', '--warning-soft', '--info', '--info-soft',
        '--inverse', '--inverse-border', '--inverse-text',
        '--color-dark-rgb', '--backdrop', '--shadow-base', '--shadow-medium',
        '--focus-ring', '--swal2-background', '--swal2-color',
        '--swal2-validation-message-background', '--swal2-validation-message-color'
    ];

    var BASE_CSS = [
        '@media (min-width: 721px) {',
        '  :root {',
        '    --font-size-sm: calc(var(--lsb-base-font-size) - 2px) !important;',
        '    --font-size-md: var(--lsb-base-font-size) !important;',
        '    --font-size-lg: calc(var(--lsb-base-font-size) + 1px) !important;',
        '  }',
        '  body { font-size: var(--lsb-base-font-size) !important; }',
        '  .top .bar {',
        '    width: min(var(--lsb-wide-max), calc(100vw - 48px)) !important;',
        '    max-width: none !important;',
        '    min-height: var(--lsb-header-height) !important;',
        '    padding: 0 20px !important;',
        '    grid-template-rows: var(--lsb-header-height) !important;',
        '    column-gap: 16px !important;',
        '  }',
        '  .top .brand { font-size: var(--lsb-base-font-size) !important; }',
        '  .top .brand::before { width: 28px !important; height: 28px !important; flex-basis: 28px !important; }',
        '  .top .search-form { width: 280px !important; height: 36px !important; }',
        '  main.wrap, .footer, .forum-more-panel {',
        '    width: min(var(--lsb-wide-max), calc(100vw - 48px)) !important;',
        '    max-width: none !important;',
        '  }',
        '  main.wrap { padding: 18px 0 28px !important; }',
        '  .home-shell { padding: var(--lsb-shell-padding) !important; }',
        '  .home-shell .forum-layout, .forum-layout { gap: var(--lsb-column-gap) !important; }',
        '  .home-shell .forum-layout {',
        '    display: grid !important; grid-template-columns: minmax(0, 1fr) var(--lsb-sidebar-width) !important;',
        '    align-items: start !important;',
        '  }',
        '  .home-shell .forum-main { grid-column: 1 !important; grid-row: 1 !important; }',
        '  .home-shell .sidebar { grid-column: 2 !important; grid-row: 1 !important; }',
        '  .home-shell .forum-layout.lsb-home-sidebar-swapped {',
        '    grid-template-columns: var(--lsb-sidebar-width) minmax(0, 1fr) !important;',
        '  }',
        '  .home-shell .forum-layout.lsb-home-sidebar-swapped > .forum-main { grid-column: 2 !important; grid-row: 1 !important; }',
        '  .home-shell .forum-layout.lsb-home-sidebar-swapped > .sidebar { grid-column: 1 !important; grid-row: 1 !important; }',
        '  .home-shell .forum-layout.lsb-home-personalized-layout { row-gap: 0 !important; }',
        '  .home-shell .forum-layout.lsb-home-personalized-layout > #lsb-home-personalization {',
        '    grid-column: 1 / -1 !important; grid-row: 1 !important; min-width: 0; width: 100%; box-sizing: border-box;',
        '  }',
        '  .home-shell .forum-layout.lsb-home-personalized-layout > .forum-main { grid-column: 1 !important; grid-row: 2 !important; }',
        '  .home-shell .forum-layout.lsb-home-personalized-layout > .sidebar { grid-column: 2 !important; grid-row: 2 !important; }',
        '  .home-shell .forum-layout.lsb-home-personalized-layout.lsb-home-sidebar-swapped > .forum-main { grid-column: 2 !important; grid-row: 2 !important; }',
        '  .home-shell .forum-layout.lsb-home-personalized-layout.lsb-home-sidebar-swapped > .sidebar { grid-column: 1 !important; grid-row: 2 !important; }',
        '  .sidebar { width: var(--lsb-sidebar-width) !important; }',
        '  .post-item { min-height: 64px !important; padding-top: 13px !important; padding-bottom: 13px !important; }',
        '  .post-title, .post-content, .notification-content { font-size: var(--lsb-base-font-size) !important; }',
        '  .post-content { line-height: 1.75 !important; }',
        '  .post-meta, .topic-pages, .post-content-stats, .profile-exit small {',
        '    font-size: calc(var(--lsb-base-font-size) - 2px) !important;',
        '  }',
        '  h2 { font-size: calc(var(--lsb-base-font-size) + 3px) !important; }',
        '  h3 { font-size: calc(var(--lsb-base-font-size) + 1px) !important; }',
        '}',
        '@media (min-width: 721px) and (max-width: 1180px) {',
        '  .top .bar, main.wrap, .footer, .forum-more-panel { width: calc(100vw - 24px) !important; }',
        '}',
        '#lsb-home-personalization {',
        '  display: grid; justify-items: center; gap: 0; padding: 42px 20px 38px;',
        '  margin: -4px 0 20px; border-bottom: 1px solid var(--line-soft); text-align: center;',
        '}',
        '#lsb-home-personalization .lsb-home-logo {',
        '  display: block; width: clamp(64px, 7vw, 96px); height: auto; margin: 0 0 14px;',
        '  filter: drop-shadow(0 12px 24px rgba(0,0,0,.24));',
        '}',
        '#lsb-home-personalization .lsb-home-tagline {',
        '  margin: 0; color: var(--text); font-size: clamp(18px, 2.1vw, 28px);',
        '  font-weight: 700; letter-spacing: .08em; line-height: 1.25;',
        '}',
        '#lsb-home-personalization .lsb-home-search-form {',
        '  grid-column: auto; grid-row: auto; justify-self: center; display: flex;',
        '  width: min(720px, 100%); height: 46px; margin: 24px auto 0;',
        '  border: 1px solid var(--line); border-radius: var(--radius);',
        '  background: var(--panel); box-shadow: 0 10px 28px var(--shadow-base);',
        '}',
        '#lsb-home-personalization .lsb-home-search-form .search-input { font-size: var(--font-size-md); }',
        '#lsb-home-personalization .lsb-home-search-form .search-field { min-width: 76px; }',
        '#lsb-home-personalization[hidden] { display: none !important; }',
        '/* 主题首帖与评论建立清晰层级:首帖突出,评论缩进显示。 */',
        '.topic-post-list { display: grid; gap: 0; }',
        '.topic-post-list > .post-entry { box-sizing: border-box; }',
        '.topic-post-list > .post-entry:not([data-floor]) {',
        '  position: relative; margin: 0 0 14px; padding: 18px 20px !important;',
        '  border: 1px solid var(--line); border-left: 4px solid var(--brand);',
        '  border-radius: var(--radius); background: var(--panel) !important;',
        '  box-shadow: 0 10px 24px var(--shadow-base);',
        '}',
        '.topic-post-list > .post-entry:not([data-floor]) .post-head::before {',
        '  content: "楼主"; display: inline-flex; align-items: center; flex: 0 0 auto;',
        '  min-height: 20px; margin-right: 8px; padding: 0 7px; border-radius: 999px;',
        '  background: var(--brand-soft); color: var(--brand-hover); font-size: 11px; font-weight: 700;',
        '}',
        '.topic-post-list > .post-entry:not([data-floor]) .post-content {',
        '  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line-soft);',
        '}',
        '.topic-post-list > .post-entry[data-floor] {',
        '  position: relative; grid-template-columns: 36px minmax(0, 1fr);',
        '  grid-template-areas: "avatar body" ". content"; column-gap: 12px; row-gap: 7px;',
        '  margin: 0 0 0 clamp(18px, 3vw, 42px); padding: 14px 0 !important; border: 0;',
        '  border-radius: 0; background: transparent !important; box-shadow: none;',
        '  transition: color .18s ease;',
        '}',
        '.topic-post-list > .post-entry[data-floor]::after {',
        '  content: ""; position: absolute; right: 0; bottom: 0; left: 0; height: 1px;',
        '  background: linear-gradient(90deg, var(--brand) 0, var(--brand) 28px, var(--line) 28px, var(--line-soft) 72%, transparent 100%);',
        '  opacity: .42; pointer-events: none; transition: opacity .18s ease;',
        '}',
        '.topic-post-list > .post-entry[data-floor]:hover {',
        '  background: transparent !important; box-shadow: none;',
        '}',
        '.topic-post-list > .post-entry[data-floor]:hover::after { opacity: .72; }',
        '.topic-post-list > .post-entry[data-floor]:last-child::after { display: none; }',
        '.topic-post-list > .post-entry[data-floor] .post-head::before {',
        '  content: "评论"; display: inline-flex; align-items: center; flex: 0 0 auto;',
        '  min-height: 19px; margin-right: 7px; padding: 0 6px; border: 1px solid var(--line);',
        '  border-radius: 999px; color: var(--text-subtle); font-size: 10px; font-weight: 700;',
        '}',
        '.topic-post-list > .post-entry[data-floor] .post-author { color: var(--text); font-weight: 700; }',
        '.topic-post-list > .post-entry[data-floor] .post-meta { margin-top: 5px; }',
        '.topic-post-list > .post-entry[data-floor] .post-content {',
        '  width: auto; margin: 0; padding: 0; border: 0; border-radius: 0; background: transparent;',
        '}',
        '.topic-post-list > .post-entry[data-floor] .post-head.has-floor { min-height: 30px; padding-right: 176px !important; }',
        '.topic-post-list > .post-entry[data-floor] .post-ops { gap: 6px; }',
        '.topic-post-list > .post-entry[data-floor] .icon-action { width: 30px; height: 30px; opacity: .7; }',
        '.topic-post-list > .post-entry[data-floor] .icon-action::before { width: 18px; height: 18px; background-size: 18px 18px; }',
        '.topic-post-list > .post-entry[data-floor] .icon-quote::before { -webkit-mask-size: 18px 18px; mask-size: 18px 18px; }',
        '.topic-post-list > .post-entry[data-floor] .content-report-icon { width: 18px; height: 18px; }',
        '.topic-post-list > .post-entry[data-floor] .like-coin-form .like-coin-action {',
        '  min-width: 30px; height: 30px; min-height: 30px; padding: 0 4px; gap: 3px; font-size: 13px; opacity: .7;',
        '}',
        '.topic-post-list > .post-entry[data-floor] .like-coin-icon { width: 18px; height: 18px; flex-basis: 18px; }',
        '.topic-post-list > .post-entry[data-floor] .post-floor {',
        '  order: initial; min-width: 30px; height: 30px; padding: 0 3px; border: 0; border-radius: 0;',
        '  background: transparent; color: var(--text-muted); font-size: 14px; font-weight: 700;',
        '  font-variant-numeric: tabular-nums;',
        '}',
        '/* 发帖编辑器插件的浅色回退值改为跟随当前深色主题。 */',
        '.topic-type-group .topic-type-choice,',
        '.topic-type-panels > .topic-type-panel,',
        '.lottery-prize-row, .lottery-card, .lottery-condition {',
        '  background: var(--card-bg) !important; color: var(--text) !important;',
        '}',
        '.lottery-rule-note { background: var(--brand-soft) !important; color: var(--text-muted) !important; }',
        '.lottery-title-status, .lottery-result { background: var(--warning-soft) !important; color: var(--warning) !important; }',
        '.lottery-title-status.drawn {',
        '  background: var(--success-soft) !important; color: var(--success) !important;',
        '}',
        '.topic-type-group .topic-type-choice strong, .topic-type-group-title,',
        '.lottery-prize-head, .lottery-prize-row label, .lottery-card strong { color: var(--text) !important; }',
        '.topic-type-group .topic-type-choice small, .lottery-prize-row small, .lottery-card header span,',
        '.lottery-card header em, .lottery-prizes span, .virtual-card-tip { color: var(--text-muted) !important; }',
        '@media (max-width: 720px) {',
        '  .topic-post-list { gap: 0; }',
        '  .topic-post-list > .post-entry:not([data-floor]) { padding: 14px !important; }',
        '  .topic-post-list > .post-entry[data-floor] {',
        '    grid-template-columns: 32px minmax(0, 1fr); column-gap: 9px;',
        '    margin-left: 8px; padding: 12px 0 !important;',
        '  }',
        '  .topic-post-list > .post-entry[data-floor] .post-head.has-floor { padding-right: 168px !important; }',
        '}',
        '/* 每个标签独立圆角;四角统一封顶 12px,避免站点的首尾伪类把单个标签压成胶囊。 */',
        '.topic-toolbar > .tab-bar {',
        '  display: inline-flex !important; flex: 0 0 auto !important; align-items: stretch !important;',
        '  gap: 5px !important; width: max-content !important; max-width: 100% !important;',
        '  border-radius: 0 !important; overflow: visible !important;',
        '  isolation: auto !important; transform: none !important;',
        '}',
        '.topic-toolbar > .tab-bar > a.tab,',
        '.topic-toolbar > .tab-bar > a.tab:first-child,',
        '.topic-toolbar > .tab-bar > a.tab:last-child,',
        '.topic-toolbar > .tab-bar > a.tab:not(:has(~ .tab)),',
        '.topic-toolbar > .tab-bar > a.tab.active {',
        '  display: inline-flex !important; align-items: center !important; justify-content: center !important;',
        '  min-height: 27px !important; line-height: 1.1 !important; box-sizing: border-box !important;',
        '  border-radius: var(--lsb-tab-radius) !important;',
        '  border-top-left-radius: var(--lsb-tab-radius) !important;',
        '  border-top-right-radius: var(--lsb-tab-radius) !important;',
        '  border-bottom-right-radius: var(--lsb-tab-radius) !important;',
        '  border-bottom-left-radius: var(--lsb-tab-radius) !important;',
        '  margin-left: 0 !important; overflow: hidden !important; background-clip: padding-box !important;',
        '}',
        '.topic-toolbar > .tab-bar > a.tab + a.tab { margin-left: 0 !important; }',
        '/* 搜索框内部控件统一字号、留白和圆角。 */',
        '.search-form {',
        '  box-sizing: border-box; border-radius: var(--lsb-search-radius) !important; overflow: hidden;',
        '}',
        '.search-field {',
        '  flex: 0 0 82px !important; height: calc(100% - 8px) !important;',
        '  margin: 4px 0 4px 4px !important; padding: 0 28px 0 12px !important;',
        '  border: 1px solid var(--line-soft) !important;',
        '  border-radius: var(--lsb-search-radius) !important;',
        '  background-color: var(--panel) !important; color: var(--text) !important;',
        '  font-size: var(--lsb-base-font-size) !important; line-height: 1 !important;',
        '  appearance: none; -webkit-appearance: none;',
        '  background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2714%27 height=%2714%27 viewBox=%270 0 14 14%27 fill=%27none%27%3E%3Cpath d=%27M3.5 5.5 7 9l3.5-3.5%27 stroke=%27%237f7f7f%27 stroke-width=%271.4%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27/%3E%3C/svg%3E");',
        '  background-repeat: no-repeat; background-position: right 9px center; background-size: 14px;',
        '}',
        '.search-input { padding: 0 14px !important; font-size: var(--lsb-base-font-size) !important; }',
        '.search-btn { width: 48px !important; padding: 0 12px !important; display: inline-flex; align-items: center; justify-content: center; border-radius: 0 var(--lsb-search-radius) var(--lsb-search-radius) 0 !important; }',
        '.search-btn svg { width: 19px !important; height: 19px !important; }',
        '.search-field option { background: var(--panel); color: var(--text); }',
        '.search-form:has(.lsb-search-select) { overflow: visible !important; }',
        '.lsb-search-select { position: relative; flex: 0 0 88px; height: 100%; z-index: 3; }',
        '.lsb-search-native {',
        '  position: absolute !important; width: 1px !important; height: 1px !important;',
        '  opacity: 0 !important; pointer-events: none !important; clip: rect(0 0 0 0) !important;',
        '}',
        '.lsb-search-select-trigger {',
        '  position: relative; display: flex; align-items: center; width: 100%; height: 100%;',
        '  padding: 0 26px 0 12px; border: 0; border-right: 1px solid var(--line-soft);',
        '  border-radius: var(--lsb-search-radius) 0 0 var(--lsb-search-radius);',
        '  background: var(--panel); color: var(--text); font: inherit; font-size: var(--lsb-base-font-size);',
        '  text-align: left; cursor: pointer;',
        '}',
        '.lsb-search-select-trigger::after {',
        '  content: ""; position: absolute; right: 10px; top: 50%; width: 7px; height: 7px;',
        '  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;',
        '  opacity: .72; transform: translateY(-65%) rotate(45deg);',
        '}',
        '.lsb-search-select-trigger[aria-expanded="true"]::after { transform: translateY(-30%) rotate(225deg); }',
        '.lsb-search-options {',
        '  position: absolute; top: calc(100% + 6px); left: 0; width: 132px; padding: 5px;',
        '  border: 1px solid var(--line); border-radius: var(--lsb-search-radius);',
        '  background: var(--panel); box-shadow: 0 14px 34px var(--shadow-medium);',
        '}',
        '.lsb-search-options[hidden] { display: none !important; }',
        '.lsb-search-option {',
        '  display: block; width: 100%; min-height: 32px; padding: 6px 9px; border: 0;',
        '  border-radius: min(var(--radius-sm), 8px); background: transparent; color: var(--text-muted);',
        '  font: inherit; font-size: var(--lsb-base-font-size); text-align: left; cursor: pointer;',
        '}',
        '.lsb-search-option:hover, .lsb-search-option[aria-selected="true"] {',
        '  background: var(--brand-soft); color: var(--text);',
        '}',
        'html, body, .top, .card, .box, .home-shell, .main-panel, .sidebar-card {',
        '  transition: background-color .18s ease, border-color .18s ease, color .18s ease;',
        '}',
        '/* 自定义面板:保留原有交互结构,仅对排版和控件做轻量优化。 */',
        '#lsb-layout-toggle {',
        '  --lsb-ui-accent: var(--brand, #b8b8b8); --lsb-ui-text: var(--text, #eeeeee);',
        '  --lsb-ui-panel: var(--panel, #1b1b1b); --lsb-ui-line: var(--line, #343434);',
        '  position: fixed; left: 22px; bottom: 22px; z-index: 2147483646;',
        '  display: inline-flex; align-items: center; justify-content: center;',
        '  width: 44px; height: 44px; padding: 0; border: 1px solid var(--lsb-ui-line);',
        '  border-radius: 12px; background: var(--lsb-ui-panel); color: var(--lsb-ui-text);',
        '  box-shadow: 0 6px 18px var(--shadow-base, rgba(0,0,0,.28));',
        '  cursor: pointer; touch-action: none; transition: transform .18s ease, border-color .18s ease, background .18s ease;',
        '}',
        '#lsb-layout-toggle.lsb-toggle-dragging { cursor: grabbing; transform: none !important; transition: none !important; }',
        '#lsb-layout-toggle:hover { transform: scale(1.05); border-color: var(--lsb-ui-accent); background: var(--brand-soft, var(--lsb-ui-panel)); }',
        '#lsb-layout-toggle[aria-expanded="true"] { border-color: var(--lsb-ui-accent); background: var(--brand-soft, var(--lsb-ui-panel)); }',
        '#lsb-layout-toggle:focus-visible, #lsb-layout-panel :focus-visible { outline: 2px solid var(--lsb-ui-accent); outline-offset: 2px; }',
        '#lsb-layout-toggle svg { width: 20px; height: 20px; }',
        '#lsb-layout-panel {',
        '  --lsb-ui-accent: var(--brand, #b8b8b8); --lsb-ui-text: var(--text, #eeeeee);',
        '  --lsb-ui-panel: var(--panel, #1b1b1b); --lsb-ui-line: var(--line, #343434);',
        '  --lsb-ui-line-soft: var(--line-soft, #272727); --lsb-ui-muted: var(--text-muted, #b6b6b6);',
        '  position: fixed; right: 22px; bottom: 76px; z-index: 2147483647;',
        '  width: min(360px, calc(100vw - 24px)); max-height: calc(100vh - 163px);',
        '  overflow: auto; color: var(--lsb-ui-text); background: var(--lsb-ui-panel);',
        '  border: 1px solid var(--lsb-ui-line); border-radius: 14px;',
        '  box-shadow: 0 18px 46px var(--shadow-medium, rgba(0,0,0,.48));',
        '  font: 13px/1.45 -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;',
        '  scrollbar-color: var(--lsb-ui-muted) transparent;',
        '}',
        '#lsb-layout-panel[hidden] { display: none !important; }',
        '/* 侧栏对调:切换按钮与面板位置联动 */',
        '.lsb-toggle-left { left: 22px !important; right: auto !important; }',
        '.lsb-toggle-right { right: 22px !important; left: auto !important; }',
        '.lsb-panel-left { left: 22px !important; right: auto !important; }',
        '.lsb-panel-right { right: 22px !important; left: auto !important; }',
        '.lsb-panel-head {',
        '  position: sticky; top: 0; z-index: 2; display: flex; align-items: center;',
        '  justify-content: space-between; gap: 7px; padding: 15px 16px 13px;',
        '  border-bottom: 1px solid var(--lsb-ui-line); background: var(--lsb-ui-panel);',
        '  cursor: move; user-select: none; touch-action: none;',
        '}',
        '.lsb-panel-head button { cursor: pointer; }',
        '#lsb-layout-panel.lsb-dragging { transition: none !important; user-select: none; }',
        '.lsb-panel-title { display: grid; gap: 2px; }',
        '.lsb-panel-title strong { color: var(--lsb-ui-text); font-size: 15px; letter-spacing: .01em; }',
        '.lsb-panel-title span { color: var(--lsb-ui-muted); font-size: 11px; }',
        '.lsb-icon-button {',
        '  display: inline-flex; align-items: center; justify-content: center;',
        '  width: 22px; height: 22px; padding: 0; border: 0;',
        '  border-radius: 0; background: transparent; color: var(--lsb-ui-muted); font-size: 16px; line-height: 1; cursor: pointer;',
        '}',
        '.lsb-panel-credit { color: var(--lsb-ui-muted); font-size: 10px; line-height: 1; opacity: .72; white-space: nowrap; }',
        '.lsb-panel-credit a { color: inherit; text-decoration: none; }',
        '.lsb-panel-body { display: grid; gap: 17px; padding: 15px 16px 17px; }',
        '.lsb-section { display: grid; gap: 11px; }',
        '.lsb-section-title {',
        '  display: flex; align-items: center; gap: 8px; margin: 0;',
        '  color: var(--lsb-ui-muted); font-size: 10px; line-height: 1.2; font-weight: 700;',
        '  letter-spacing: .1em; text-transform: uppercase;',
        '}',
        '.lsb-section-title::after { content: ""; height: 1px; flex: 1; background: var(--lsb-ui-line-soft); }',
        '.lsb-range-row { display: grid; gap: 7px; }',
        '.lsb-range-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }',
        '.lsb-range-label, .lsb-field > span { color: var(--lsb-ui-muted); font-size: 12px; }',
        '.lsb-range-value { min-width: 54px; color: var(--lsb-ui-text); font-size: 12px; font-variant-numeric: tabular-nums; text-align: right; }',
        '.lsb-range { width: 100%; height: 18px; margin: 0; accent-color: var(--lsb-ui-accent); cursor: pointer; }',
        '.lsb-field { display: grid; gap: 7px; }',
        '.lsb-select, .lsb-textarea {',
        '  width: 100%; border: 1px solid var(--lsb-ui-line); border-radius: 8px;',
        '  background: var(--bg-soft, var(--lsb-ui-panel)); color: var(--lsb-ui-text); font: inherit; box-sizing: border-box;',
        '}',
        '.lsb-select { height: 38px; padding: 0 10px; cursor: pointer; }',
        '.lsb-select option { background: var(--lsb-ui-panel); color: var(--lsb-ui-text); }',
        '.lsb-select:focus, .lsb-textarea:focus { outline: none; border-color: var(--lsb-ui-accent); box-shadow: 0 0 0 2px var(--focus-ring, rgba(184,184,184,.34)); }',
        '.lsb-color-line {',
        '  display: grid; grid-template-columns: 42px 1fr; align-items: center; gap: 10px;',
        '  padding: 7px 9px; border: 1px solid var(--lsb-ui-line); border-radius: 9px; background: var(--bg-soft, var(--lsb-ui-panel));',
        '}',
        '.lsb-color-input { width: 42px; height: 30px; padding: 2px; border: 0; border-radius: 7px; background: transparent; cursor: pointer; }',
        '.lsb-color-value { color: var(--lsb-ui-muted); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }',
        '.lsb-check-line { display: grid; grid-template-columns: 34px minmax(0, 1fr); align-items: center; gap: 10px; min-height: 26px; color: var(--lsb-ui-muted); cursor: pointer; }',
        '.lsb-check-line input { position: relative; width: 34px; height: 19px; margin: 0; border: 1px solid var(--lsb-ui-line); border-radius: 999px; background: var(--lsb-ui-line-soft); cursor: pointer; appearance: none; -webkit-appearance: none; transition: background-color .16s ease, border-color .16s ease; }',
        '.lsb-check-line input::after { content: ""; position: absolute; top: 3px; left: 3px; width: 11px; height: 11px; border-radius: 50%; background: var(--lsb-ui-muted); transition: left .16s ease, background-color .16s ease; }',
        '.lsb-check-line input:checked { border-color: var(--lsb-ui-accent); background: var(--lsb-ui-accent); }',
        '.lsb-check-line input:checked::after { left: 18px; background: var(--inverse, #111111); }',
        '.lsb-check-line input:focus-visible { outline: 2px solid var(--lsb-ui-accent); outline-offset: 2px; }',
        '.lsb-check-line input:checked + span { color: var(--lsb-ui-text); }',
        '.lsb-check-line span { line-height: 1.45; }',
        '.lsb-theme-note { margin: 0; color: var(--lsb-ui-muted); font-size: 11px; line-height: 1.55; }',
        '.lsb-filter-section { margin-top: 3px; }',
        '.lsb-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }',
        '.lsb-button {',
        '  min-height: 38px; padding: 0 12px; border: 1px solid var(--lsb-ui-line); border-radius: 9px;',
        '  background: transparent; color: var(--lsb-ui-text); font: inherit; font-weight: 600; cursor: pointer;',
        '}',
        '.lsb-button:hover { border-color: var(--lsb-ui-accent); background: var(--brand-soft, transparent); }',
        '.lsb-button-primary { border-color: var(--lsb-ui-accent); background: var(--brand-soft, transparent); }',
        '.lsb-status { min-height: 16px; margin: 0; color: var(--lsb-ui-muted); font-size: 11px; text-align: center; }',
        '.lsb-input { width: 100%; height: 38px; padding: 0 10px; border: 1px solid var(--lsb-ui-line); border-radius: 8px; background: var(--bg-soft, var(--lsb-ui-panel)); color: var(--lsb-ui-text); font: inherit; box-sizing: border-box; }',
        '.lsb-input:focus { outline: none; border-color: var(--lsb-ui-accent); box-shadow: 0 0 0 2px var(--focus-ring, rgba(184,184,184,.34)); }',
        '.lsb-check-line-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }',
        '.lsb-upload-settings-toggle { display: inline-flex; align-items: center; justify-content: center; min-height: 24px; padding: 0 8px; border: 1px solid var(--lsb-ui-line); border-radius: 6px; background: transparent; color: var(--lsb-ui-muted); font: 10px/1 inherit; cursor: pointer; }',
        '.lsb-upload-settings-toggle:hover { border-color: var(--lsb-ui-accent); color: var(--lsb-ui-text); }',
        '.lsb-upload-settings-toggle[hidden], .lsb-upload-settings[hidden] { display: none !important; }',
        '.lsb-upload-settings { display: grid; gap: 10px; margin: 2px 0 2px 44px; padding: 10px; border-left: 1px solid var(--lsb-ui-line-soft); }',
        '.lsb-upload-hint { margin: 0; color: var(--lsb-ui-muted); font-size: 10px; line-height: 1.5; }',
        '.lsb-image-upload-button { display: inline-flex; align-items: center; justify-content: center; min-height: 28px; margin: 7px 0 0; padding: 0 9px; border: 1px dashed var(--line, #343434); border-radius: 7px; background: transparent; color: var(--text-muted, #b6b6b6); font: 12px/1 inherit; cursor: pointer; }',
        '.lsb-image-upload-button:hover, .lsb-image-upload-button:focus-visible { border-color: var(--brand, #b8b8b8); color: var(--text, #eeeeee); outline: none; }',
        '.lsb-image-upload-button[disabled] { opacity: .65; cursor: wait; }',
        '.lsb-image-upload-drop-target { outline: 2px dashed var(--brand, #b8b8b8); outline-offset: 3px; }',
        '.lsb-image-upload-hint { margin: 5px 0 0; color: var(--text-muted, #b6b6b6); font-size: 11px; }',
        '#lsb-image-lightbox { position: fixed; inset: 0; z-index: 2147483645; display: flex; align-items: center; justify-content: center; padding: 24px; background: var(--backdrop, rgba(0,0,0,.68)); cursor: zoom-out; }',
        '#lsb-image-lightbox[hidden] { display: none !important; }',
        '#lsb-image-lightbox .lsb-lightbox-image { max-width: min(1200px, 100%); max-height: calc(100vh - 48px); border: 1px solid var(--line, #343434); border-radius: var(--radius, 6px); background: var(--panel, #1b1b1b); box-shadow: 0 20px 52px var(--shadow-medium, rgba(0,0,0,.48)); cursor: default; object-fit: contain; }',
        '#lsb-image-lightbox .lsb-lightbox-close { position: absolute; top: 14px; right: 16px; width: 30px; height: 30px; padding: 0; border: 1px solid var(--line, #343434); border-radius: 8px; background: var(--panel, #1b1b1b); color: var(--text, #eeeeee); font-size: 20px; line-height: 1; cursor: pointer; }',
        '.post-entry .post-content img[data-lsb-lightbox-image], .post-item .post-content img[data-lsb-lightbox-image] { cursor: zoom-in; }',
        'body.lsb-lightbox-open { overflow: hidden !important; }',
        '@media (max-width: 520px) {',
        '  #lsb-layout-toggle { right: 12px; bottom: 12px; }',
        '  #lsb-layout-panel { right: 12px; bottom: 66px; max-height: calc(100vh - 82px); }',
        '}',
        '@media (prefers-reduced-motion: reduce) {',
        '  #lsb-layout-toggle, html, body, .top, .card, .box, .home-shell, .main-panel, .sidebar-card { transition-duration: .01ms !important; }',
        '}',
        '.lsb-textarea { padding: 8px 10px; font-size: 12px; line-height: 1.5; resize: vertical; }'
    ].join('\n');

    var panel = null;
    var toggleButton = null;
    var statusElement = null;
    var saveTimer = 0;
    var suppressToggleClick = false;
    var searchDropdownEventsBound = false;
    var searchDropdownId = 0;
    var homeObserver = null;
    var imageLightbox = null;
    var imageLightboxImage = null;
    var imageLightboxEventsBound = false;
    var imageUploadBusyEditors = new WeakSet();
    var autoCheckinInFlight = false;
    var autoCheckinStartTimer = 0;
    var settings = loadSettings();

    addStyle(buildStartupCss(settings) + '\n' + BASE_CSS);
    applySettings();
    startHomeObserver();

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', ensureInterface, { once: true });
    } else {
        ensureInterface();
    }

    if (typeof GM_registerMenuCommand === 'function') {
        GM_registerMenuCommand('打开 LINUX SB 布局设置', function () {
            ensureInterface();
            setPanelOpen(true);
        });
    }

    function addStyle(cssText) {
        if (typeof GM_addStyle === 'function') {
            GM_addStyle(cssText);
            return;
        }

        var style = document.createElement('style');
        style.id = 'linux-sb-wide-layout-style';
        style.textContent = cssText;
        (document.head || document.documentElement).appendChild(style);
    }

    function buildStartupCss(currentSettings) {
        var variables = {
            '--lsb-wide-max': currentSettings.maxWidth + 'px',
            '--lsb-header-height': currentSettings.headerHeight + 'px',
            '--lsb-base-font-size': currentSettings.fontSize + 'px',
            '--lsb-radius': currentSettings.radius + 'px',
            '--radius': currentSettings.radius + 'px',
            '--radius-sm': Math.max(0, currentSettings.radius - 2) + 'px',
            '--lsb-tab-radius': Math.min(currentSettings.radius, 12) + 'px',
            '--lsb-search-radius': Math.min(currentSettings.radius, 12) + 'px',
            '--lsb-sidebar-width': currentSettings.sidebarWidth + 'px',
            '--lsb-shell-padding': currentSettings.shellPadding + 'px',
            '--lsb-column-gap': currentSettings.columnGap + 'px',
            '--bg-soft': 'var(--bg)',
            '--card-bg': 'var(--panel)'
        };
        var theme = THEMES[currentSettings.theme] || THEMES.neutral;

        if (theme.vars) {
            Object.keys(theme.vars).forEach(function (name) {
                variables[name] = theme.vars[name];
            });
        }

        var accentRgb = hexToRgb(currentSettings.accent);
        var background = theme.vars && theme.vars['--bg'] ? theme.vars['--bg'] : '#1a1b2e';
        variables['--brand'] = currentSettings.accent;
        variables['--brand-hover'] = mixHex(currentSettings.accent, '#ffffff', 0.18);
        variables['--brand-soft'] = 'rgba(' + accentRgb.r + ',' + accentRgb.g + ',' + accentRgb.b + ',.16)';
        variables['--focus-ring'] = 'rgba(' + accentRgb.r + ',' + accentRgb.g + ',' + accentRgb.b + ',.34)';
        variables['--text'] = currentSettings.textColor;
        variables['--text-muted'] = mixHex(currentSettings.textColor, background, 0.34);
        variables['--text-subtle'] = mixHex(currentSettings.textColor, background, 0.55);
        variables['--text-disabled'] = mixHex(currentSettings.textColor, background, 0.72);

        if (currentSettings.theme !== 'original') {
            variables['--swal2-background'] = 'var(--panel)';
            variables['--swal2-color'] = 'var(--text)';
            variables['--swal2-validation-message-background'] = 'var(--line-soft)';
            variables['--swal2-validation-message-color'] = 'var(--text-muted)';
        }

        return [
            ':root:not([data-lsb-ready]) {',
            Object.keys(variables).map(function (name) {
                return '  ' + name + ': ' + variables[name] + ' !important;';
            }).join('\n'),
            '}'
        ].join('\n');
    }

    function startHomeObserver() {
        if (homeObserver || typeof MutationObserver !== 'function') {
            return;
        }

        homeObserver = new MutationObserver(function () {
            applyHomePersonalization();
            enhanceSearchFields(document);
            enforceRadiusOverrides();
            scheduleFilter();
            updateImageLightboxTargets();
            updateImageUploadTargets();
        });
        homeObserver.observe(document, { childList: true, subtree: true });
    }

    function normalizeSettings(value) {
        var source = value && typeof value === 'object' ? value : {};
        var result = Object.assign({}, DEFAULTS, source);

        RANGE_DEFINITIONS.forEach(function (definition) {
            var number = Number(result[definition.key]);
            if (!Number.isFinite(number)) {
                number = DEFAULTS[definition.key];
            }
            result[definition.key] = Math.min(definition.max, Math.max(definition.min, number));
        });

        if (!Object.prototype.hasOwnProperty.call(THEMES, result.theme)) {
            result.theme = DEFAULTS.theme;
        }

        if (!Object.prototype.hasOwnProperty.call(TEXT_PALETTES, result.textPalette)) {
            result.textPalette = DEFAULTS.textPalette;
        }

        result.homePersonalized = result.homePersonalized === true || result.homePersonalized === 'true';
        result.autoCheckin = result.autoCheckin === true || result.autoCheckin === 'true';
        result.autoCheckinLastDate = /^\d{4}-\d{2}-\d{2}$/.test(String(result.autoCheckinLastDate || '')) ? String(result.autoCheckinLastDate) : '';
        result.imageLightbox = result.imageLightbox === true || result.imageLightbox === 'true';
        result.imageUpload = result.imageUpload === true || result.imageUpload === 'true';
        result.imageUploadProvider = ['imgur', 'nodeimage', 'custom'].indexOf(result.imageUploadProvider) >= 0 ? result.imageUploadProvider : 'imgur';
        result.imageUploadHost = normalizeUploadText(result.imageUploadHost, DEFAULTS.imageUploadHost, 512);
        result.imageUploadEndpoint = normalizeUploadText(result.imageUploadEndpoint, DEFAULTS.imageUploadEndpoint, 1024);
        result.imageUploadFileField = normalizeUploadField(result.imageUploadFileField, DEFAULTS.imageUploadFileField);
        result.imageUploadResponsePath = normalizeUploadResponsePath(result.imageUploadResponsePath, DEFAULTS.imageUploadResponsePath);
        result.imageUploadAuthMode = ['none', 'imgur-client-id', 'nodeimage-api-key', 'bearer'].indexOf(result.imageUploadAuthMode) >= 0 ? result.imageUploadAuthMode : DEFAULTS.imageUploadAuthMode;
        result.imageUploadToken = normalizeUploadText(result.imageUploadToken, '', 512);
        result.imageUploadSettingsCollapsed = result.imageUploadSettingsCollapsed === true || result.imageUploadSettingsCollapsed === 'true';

        if (!/^#[0-9a-f]{6}$/i.test(String(result.accent))) {
            result.accent = THEMES[result.theme].accent;
        }

        result.accent = String(result.accent).toLowerCase();

        if (!/^#[0-9a-f]{6}$/i.test(String(result.textColor))) {
            result.textColor = THEMES[result.theme].textColor || DEFAULTS.textColor;
        }

        result.textColor = String(result.textColor).toLowerCase();

        ['panelLeft', 'panelTop', 'toggleLeft', 'toggleTop'].forEach(function (key) {
            var rawPosition = result[key];
            var position = Number(rawPosition);
            result[key] = rawPosition !== null && rawPosition !== undefined && rawPosition !== '' &&
                Number.isFinite(position) && position >= 0 ? Math.round(position) : null;
        });

        // 设置迁移:v1 升级到 v2,maxWidth 从 1600 调整为 1100
        if ((result.version || 0) < 2 && result.maxWidth === 1600) {
            result.maxWidth = 1100;
        }
        result.version = SETTINGS_VERSION;

        if (!Array.isArray(result.titleFilters)) {
            result.titleFilters = [];
        }

        if (!Array.isArray(result.userFilters)) {
            result.userFilters = [];
        }

        return result;
    }

    function loadSettings() {
        try {
            var stored = null;
            if (typeof GM_getValue === 'function') {
                stored = GM_getValue(STORAGE_KEY, null);
            } else {
                var raw = localStorage.getItem(STORAGE_KEY);
                stored = raw ? JSON.parse(raw) : null;
            }
            return normalizeSettings(stored);
        } catch (error) {
            return normalizeSettings(null);
        }
    }

    function persistSettings() {
        try {
            if (typeof GM_setValue === 'function') {
                GM_setValue(STORAGE_KEY, settings);
            } else {
                localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
            }
            showStatus('设置已保存');
        } catch (error) {
            showStatus('设置已应用,但保存失败');
        }
    }

    function scheduleSave() {
        window.clearTimeout(saveTimer);
        saveTimer = window.setTimeout(persistSettings, 140);
    }

    function setRootVariable(name, value) {
        if (document.documentElement) {
            document.documentElement.style.setProperty(name, value, 'important');
        }
    }

    function clearThemeVariables() {
        if (!document.documentElement) {
            return;
        }
        THEME_VARIABLES.forEach(function (name) {
            document.documentElement.style.removeProperty(name);
        });
    }

    function applySettings() {
        if (!document.documentElement) {
            return;
        }

        setRootVariable('--lsb-wide-max', settings.maxWidth + 'px');
        setRootVariable('--lsb-header-height', settings.headerHeight + 'px');
        setRootVariable('--lsb-base-font-size', settings.fontSize + 'px');
        setRootVariable('--lsb-radius', settings.radius + 'px');
        setRootVariable('--radius', settings.radius + 'px');
        setRootVariable('--radius-sm', Math.max(0, settings.radius - 2) + 'px');
        setRootVariable('--lsb-tab-radius', Math.min(settings.radius, 12) + 'px');
        setRootVariable('--lsb-search-radius', Math.min(settings.radius, 12) + 'px');
        setRootVariable('--lsb-sidebar-width', settings.sidebarWidth + 'px');
        setRootVariable('--lsb-shell-padding', settings.shellPadding + 'px');
        setRootVariable('--lsb-column-gap', settings.columnGap + 'px');
        setRootVariable('--bg-soft', 'var(--bg)');
        setRootVariable('--card-bg', 'var(--panel)');

        applyTheme();
        document.documentElement.setAttribute('data-lsb-ready', '');
        applyHomePersonalization();
        applySidebarSwap();
        enhanceSearchFields(document);
        enforceRadiusOverrides();
        applyFilters();
        applyAutoCheckin();
        applyImageLightbox();
        applyImageUpload();

        if (panel) {
            panel.style.setProperty('--lsb-ui-accent', settings.accent);
            panel.style.setProperty('--lsb-ui-text', settings.textColor);
        }
        if (toggleButton) {
            toggleButton.style.setProperty('--lsb-ui-accent', settings.accent);
            toggleButton.style.setProperty('--lsb-ui-text', settings.textColor);
        }
    }



    function normalizeUploadText(value, fallback, maxLength) {
        var normalized = value === null || value === undefined ? '' : String(value).trim();
        if (!normalized) {
            return fallback;
        }
        return normalized.slice(0, maxLength);
    }

    function normalizeUploadField(value, fallback) {
        var normalized = normalizeUploadText(value, fallback, 64);
        return /^[A-Za-z0-9_-]+$/.test(normalized) ? normalized : fallback;
    }

    function normalizeUploadResponsePath(value, fallback) {
        var normalized = normalizeUploadText(value, fallback, 120);
        return /^(?:[A-Za-z_$][A-Za-z0-9_$]*)(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(normalized) ? normalized : fallback;
    }

    function syncImageUploadControls() {
        if (!panel) {
            return;
        }

        var settingsBlock = panel.querySelector('[data-lsb-upload-settings]');
        var settingsToggle = panel.querySelector('[data-lsb-upload-settings-toggle]');
        var provider = panel.querySelector('[data-lsb-upload-provider]');
        var host = panel.querySelector('[data-lsb-upload-host]');
        var endpoint = panel.querySelector('[data-lsb-upload-endpoint]');
        var field = panel.querySelector('[data-lsb-upload-file-field]');
        var responsePath = panel.querySelector('[data-lsb-upload-response-path]');
        var authMode = panel.querySelector('[data-lsb-upload-auth-mode]');
        var token = panel.querySelector('[data-lsb-upload-token]');

        if (!settingsBlock || !settingsToggle || !provider || !host || !endpoint || !field || !responsePath || !authMode || !token) {
            return;
        }

        var expanded = settings.imageUpload && !settings.imageUploadSettingsCollapsed;
        settingsBlock.hidden = !expanded;
        settingsToggle.style.display = settings.imageUpload ? '' : 'none';
        settingsToggle.setAttribute('aria-expanded', String(expanded));
        settingsToggle.textContent = expanded ? '收起配置' : '展开配置';
        provider.value = settings.imageUploadProvider;
        host.value = settings.imageUploadHost;
        endpoint.value = settings.imageUploadEndpoint;
        field.value = settings.imageUploadFileField;
        responsePath.value = settings.imageUploadResponsePath;
        authMode.value = settings.imageUploadAuthMode;
        token.value = settings.imageUploadToken;
    }

    function applyImageUpload() {
        updateImageUploadTargets();
    }

    function updateImageUploadTargets() {
        if (!document.querySelectorAll) {
            return;
        }

        document.querySelectorAll('[data-lsb-image-upload-button]').forEach(function (button) {
            if (!settings.imageUpload || !button.previousElementSibling || !isImageUploadEditor(button.previousElementSibling)) {
                button.remove();
            }
        });

        if (!settings.imageUpload) {
            document.querySelectorAll('.lsb-image-upload-drop-target').forEach(function (editor) {
                editor.classList.remove('lsb-image-upload-drop-target');
            });
            return;
        }

        getImageUploadEditors().forEach(function (editor) {
            bindImageUploadEditor(editor);
        });
    }

    function getImageUploadEditors() {
        var result = [];
        document.querySelectorAll('textarea, [contenteditable="true"], input[data-quick-reply-action]').forEach(function (editor) {
            if (isImageUploadEditor(editor)) {
                result.push(editor);
            }
        });
        return result;
    }

    function isImageUploadEditor(editor) {
        if (!editor || !editor.isConnected || editor.closest('#lsb-layout-panel') || editor.readOnly || editor.disabled) {
            return false;
        }
        if (editor.matches('input[data-quick-reply-action]')) {
            return true;
        }
        if (editor.matches('[contenteditable="true"]')) {
            return true;
        }
        if (!editor.matches('textarea')) {
            return false;
        }
        return !editor.classList.contains('lsb-textarea');
    }

    function bindImageUploadEditor(editor) {
        if (editor.getAttribute('data-lsb-image-upload-bound') === '1') {
            ensureImageUploadButton(editor);
            return;
        }

        editor.setAttribute('data-lsb-image-upload-bound', '1');
        editor.addEventListener('dragenter', function (event) {
            if (settings.imageUpload && containsImageFiles(event.dataTransfer)) {
                event.preventDefault();
                editor.classList.add('lsb-image-upload-drop-target');
            }
        });
        editor.addEventListener('dragover', function (event) {
            if (settings.imageUpload && containsImageFiles(event.dataTransfer)) {
                event.preventDefault();
                editor.classList.add('lsb-image-upload-drop-target');
                if (event.dataTransfer) {
                    event.dataTransfer.dropEffect = 'copy';
                }
            }
        });
        editor.addEventListener('dragleave', function () {
            editor.classList.remove('lsb-image-upload-drop-target');
        });
        editor.addEventListener('drop', function (event) {
            editor.classList.remove('lsb-image-upload-drop-target');
            if (!settings.imageUpload || !containsImageFiles(event.dataTransfer)) {
                return;
            }
            event.preventDefault();
            event.stopPropagation();
            uploadImageFiles(editor, Array.from(event.dataTransfer.files || []));
        });
        editor.addEventListener('paste', function (event) {
            if (!settings.imageUpload) {
                return;
            }
            var files = getClipboardImageFiles(event.clipboardData);
            if (!files.length) {
                return;
            }
            event.preventDefault();
            event.stopPropagation();
            uploadImageFiles(editor, files);
        });

        ensureImageUploadButton(editor);
    }

    function ensureImageUploadButton(editor) {
        var button = editor.nextElementSibling;
        if (button && button.hasAttribute('data-lsb-image-upload-button')) {
            return;
        }

        button = document.createElement('button');
        button.type = 'button';
        button.className = 'lsb-image-upload-button';
        button.setAttribute('data-lsb-image-upload-button', '1');
        button.textContent = '上传图片';
        button.addEventListener('click', function () {
            selectImagesForEditor(editor);
        });
        editor.insertAdjacentElement('afterend', button);
    }

    function containsImageFiles(dataTransfer) {
        if (!dataTransfer || !dataTransfer.files || !dataTransfer.files.length) {
            return false;
        }
        return Array.from(dataTransfer.files).some(function (file) {
            return file && /^image\//i.test(file.type || '');
        });
    }

    function getClipboardImageFiles(clipboardData) {
        if (!clipboardData) {
            return [];
        }

        var files = Array.from(clipboardData.items || []).map(function (item) {
            if (!item || item.kind !== 'file' || !/^image\//i.test(item.type || '')) {
                return null;
            }
            return item.getAsFile ? item.getAsFile() : null;
        }).filter(Boolean);

        if (!files.length && clipboardData.files) {
            files = Array.from(clipboardData.files).filter(function (file) {
                return file && /^image\//i.test(file.type || '');
            });
        }
        return files;
    }

    function selectImagesForEditor(editor) {
        if (!settings.imageUpload || imageUploadBusyEditors.has(editor)) {
            return;
        }

        var picker = document.createElement('input');
        picker.type = 'file';
        picker.accept = 'image/*';
        picker.multiple = true;
        picker.hidden = true;
        picker.addEventListener('change', function () {
            uploadImageFiles(editor, Array.from(picker.files || []));
            picker.remove();
        }, { once: true });
        document.body.appendChild(picker);
        picker.click();
    }

    function uploadImageFiles(editor, files) {
        var validFiles = files.filter(function (file) {
            return file && /^image\//i.test(file.type || '') && file.size > 0;
        });
        var oversized = validFiles.filter(function (file) {
            return file.size > 10 * 1024 * 1024;
        });
        validFiles = validFiles.filter(function (file) {
            return file.size <= 10 * 1024 * 1024;
        }).slice(0, 6);

        if (!validFiles.length) {
            showStatus(oversized.length ? '图片超过 10 MB,未开始上传' : '请选择 PNG、JPG、GIF、WebP 等图片文件');
            return;
        }
        if (imageUploadBusyEditors.has(editor)) {
            return;
        }

        imageUploadBusyEditors.add(editor);
        setImageUploadButtonState(editor, '上传中…', true);
        var uploaded = 0;
        var chain = Promise.resolve();
        validFiles.forEach(function (file) {
            chain = chain.then(function () {
                return uploadOneImage(file).then(function (url) {
                    insertMarkdownImage(editor, url, file.name);
                    uploaded += 1;
                    setImageUploadButtonState(editor, '已上传 ' + uploaded + '/' + validFiles.length, true);
                });
            });
        });
        chain.then(function () {
            showStatus('已插入 ' + uploaded + ' 张图片');
        }).catch(function (error) {
            showStatus('图片上传失败:' + getUploadErrorMessage(error));
        }).finally(function () {
            imageUploadBusyEditors.delete(editor);
            setImageUploadButtonState(editor, '上传图片', false);
        });
    }

    function getImageUploadConfig() {
        var endpoint = settings.imageUploadEndpoint;
        var host = settings.imageUploadHost;
        if (!isSafeHttpsUrl(endpoint) || !isSafeHttpsUrl(host)) {
            throw new Error('请填写有效的 HTTPS 图床主页和上传接口');
        }
        if (settings.imageUploadAuthMode === 'imgur-client-id' && !settings.imageUploadToken) {
            throw new Error('Imgur 上传需要填写 Client ID');
        }
        if (settings.imageUploadAuthMode === 'nodeimage-api-key' && !settings.imageUploadToken) {
            throw new Error('Nodeimage 上传需要填写 API 密钥');
        }
        return {
            provider: settings.imageUploadProvider,
            endpoint: endpoint,
            field: settings.imageUploadFileField,
            responsePath: settings.imageUploadResponsePath,
            authMode: settings.imageUploadAuthMode,
            token: settings.imageUploadToken
        };
    }

    function isSafeHttpsUrl(value) {
        try {
            var url = new URL(String(value));
            return url.protocol === 'https:' && !url.username && !url.password;
        } catch (error) {
            return false;
        }
    }

    function uploadOneImage(file) {
        var config;
        try {
            config = getImageUploadConfig();
        } catch (error) {
            return Promise.reject(error);
        }

        var data = new FormData();
        data.append(config.field, file, file.name);
        var headers = {};
        if (config.authMode === 'imgur-client-id') {
            headers.Authorization = 'Client-ID ' + config.token;
        } else if (config.authMode === 'nodeimage-api-key') {
            headers['X-API-Key'] = config.token;
        } else if (config.authMode === 'bearer' && config.token) {
            headers.Authorization = 'Bearer ' + config.token;
        }

        if (typeof GM_xmlhttpRequest === 'function') {
            return new Promise(function (resolve, reject) {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: config.endpoint,
                    headers: headers,
                    data: data,
                    responseType: 'json',
                    timeout: 60000,
                    onload: function (response) {
                        if (response.status < 200 || response.status >= 300) {
                            reject(new Error('图床返回 HTTP ' + response.status));
                            return;
                        }
                        try {
                            resolve(resolveUploadedImageUrl(parseUploadResponse(response.response, response.responseText), config));
                        } catch (error) {
                            reject(error);
                        }
                    },
                    onerror: function () {
                        reject(new Error('网络或跨域请求失败'));
                    },
                    ontimeout: function () {
                        reject(new Error('请求超时'));
                    }
                });
            });
        }

        return fetch(config.endpoint, {
            method: 'POST',
            headers: headers,
            body: data,
            credentials: 'omit'
        }).then(function (response) {
            if (!response.ok) {
                throw new Error('图床返回 HTTP ' + response.status);
            }
            return response.text();
        }).then(function (body) {
            return resolveUploadedImageUrl(parseUploadResponse(null, body), config);
        });
    }

    function parseUploadResponse(response, responseText) {
        if (response && typeof response === 'object') {
            return response;
        }
        if (typeof responseText === 'string') {
            try {
                return JSON.parse(responseText);
            } catch (error) {
                return responseText;
            }
        }
        return response;
    }

    function resolveUploadedImageUrl(payload, config) {
        if (config.provider !== 'nodeimage') {
            return extractImageUrl(payload, config.responsePath);
        }

        // Nodeimage 的公开接口说明未公开固定返回字段,因此优先使用预设字段,再兼容常见图片直链字段。
        var paths = [config.responsePath, 'data.url', 'data.link', 'url', 'link', 'image.url', 'image.link', 'data.image.url', 'data.image.link'];
        for (var index = 0; index < paths.length; index += 1) {
            try {
                return extractImageUrl(payload, paths[index]);
            } catch (error) {
                // 尝试下一个候选字段。
            }
        }

        var fallbackUrl = findFirstSafeHttpsUrl(payload, 0);
        if (fallbackUrl) {
            return fallbackUrl;
        }
        throw new Error('未从 Nodeimage 返回结果中取得 HTTPS 图片直链');
    }

    function findFirstSafeHttpsUrl(value, depth) {
        if (depth > 5 || value === null || value === undefined) {
            return '';
        }
        if (typeof value === 'string') {
            var candidate = value.trim();
            return isSafeHttpsUrl(candidate) ? candidate : '';
        }
        if (Array.isArray(value)) {
            for (var index = 0; index < value.length; index += 1) {
                var arrayUrl = findFirstSafeHttpsUrl(value[index], depth + 1);
                if (arrayUrl) {
                    return arrayUrl;
                }
            }
            return '';
        }
        if (typeof value === 'object') {
            var keys = Object.keys(value);
            for (var keyIndex = 0; keyIndex < keys.length; keyIndex += 1) {
                var objectUrl = findFirstSafeHttpsUrl(value[keys[keyIndex]], depth + 1);
                if (objectUrl) {
                    return objectUrl;
                }
            }
        }
        return '';
    }

    function extractImageUrl(payload, path) {
        var value = payload;
        if (typeof payload === 'string') {
            value = payload.trim();
        } else {
            path.split('.').forEach(function (segment) {
                if (value && typeof value === 'object' && Object.prototype.hasOwnProperty.call(value, segment)) {
                    value = value[segment];
                } else {
                    value = null;
                }
            });
        }

        if (typeof value !== 'string' || !isSafeHttpsUrl(value)) {
            throw new Error('未从返回结果中取得 HTTPS 图片直链,请检查返回字段');
        }
        return value;
    }

    function insertMarkdownImage(editor, url, filename) {
        var alt = String(filename || '图片').replace(/[\[\]\n\r]/g, '').slice(0, 80) || '图片';
        var markdown = '![' + alt + '](' + url + ')';
        var prefix = getEditorText(editor) ? '\n' : '';
        var text = prefix + markdown + '\n';

        if (editor.matches('[contenteditable="true"]')) {
            editor.focus();
            var selection = window.getSelection();
            if (selection && selection.rangeCount && editor.contains(selection.getRangeAt(0).commonAncestorContainer)) {
                selection.getRangeAt(0).deleteContents();
                selection.getRangeAt(0).insertNode(document.createTextNode(text));
                selection.collapseToEnd();
            } else {
                editor.appendChild(document.createTextNode(text));
            }
        } else {
            var start = Number.isFinite(editor.selectionStart) ? editor.selectionStart : editor.value.length;
            var end = Number.isFinite(editor.selectionEnd) ? editor.selectionEnd : start;
            editor.focus();
            editor.setRangeText(text, start, end, 'end');
        }
        editor.dispatchEvent(new Event('input', { bubbles: true }));
        editor.dispatchEvent(new Event('change', { bubbles: true }));
    }

    function getEditorText(editor) {
        return editor.matches('[contenteditable="true"]') ? editor.textContent.trim() : String(editor.value || '').trim();
    }

    function setImageUploadButtonState(editor, text, disabled) {
        var button = editor.nextElementSibling;
        if (!button || !button.hasAttribute('data-lsb-image-upload-button')) {
            return;
        }
        button.textContent = text;
        button.disabled = disabled;
    }

    function getUploadErrorMessage(error) {
        var message = error && error.message ? String(error.message) : '未知错误';
        return message.slice(0, 120);
    }

    function getAutoCheckinDate() {
        var now = new Date();
        var month = String(now.getMonth() + 1).padStart(2, '0');
        var day = String(now.getDate()).padStart(2, '0');
        return now.getFullYear() + '-' + month + '-' + day;
    }

    function applyAutoCheckin() {
        window.clearTimeout(autoCheckinStartTimer);
        autoCheckinStartTimer = 0;

        if (!settings.autoCheckin || autoCheckinInFlight || settings.autoCheckinLastDate === getAutoCheckinDate()) {
            return;
        }

        var scheduleAttempt = function () {
            autoCheckinStartTimer = window.setTimeout(function () {
                autoCheckinStartTimer = 0;
                performAutoCheckin();
            }, 900);
        };

        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', scheduleAttempt, { once: true });
        } else {
            scheduleAttempt();
        }
    }

    function hasCompletedDailyCheckin(html) {
        // 与参考脚本一致:站点不同页面可能只显示“已签到”。
        return /今天已签到|已签到|签到成功|已完成签到/.test(String(html || ''));
    }

    function isDailyCheckinLoginPage(html) {
        var source = String(html || '');
        return /用户名或邮箱|忘记密码|<title[^>]*>[^<]*登录|name=["'](?:username|password)["']/i.test(source);
    }

    function hasDailyCheckinFailure(html) {
        // 仅识别明确的签到失败结果,普通页面中的 CSRF 隐藏字段不视为异常。
        return /签到失败|请求失败|(?:csrf|token)[^\n<]{0,32}(?:失效|错误|无效)|error\s*(?:message|:)/i.test(String(html || ''));
    }

    function extractDailyCheckinCsrf(html) {
        var source = String(html || '');
        try {
            var documentNode = new DOMParser().parseFromString(source, 'text/html');
            var input = documentNode.querySelector('input[name="_csrf"], input[name="csrf_token"], input[name="csrf"]');
            if (input && input.value) {
                return { name: input.name, value: input.value };
            }
        } catch (error) {
            // DOM 解析不可用时,继续使用正则回退。
        }

        var match = source.match(/<input\b[^>]*\bname=["'](_csrf|csrf_token|csrf)["'][^>]*\bvalue=["']([^"']+)["'][^>]*>/i) ||
            source.match(/<input\b[^>]*\bvalue=["']([^"']+)["'][^>]*\bname=["'](_csrf|csrf_token|csrf)["'][^>]*>/i);
        if (!match) {
            return null;
        }
        return match[1] === '_csrf' || match[1] === 'csrf_token' || match[1] === 'csrf' ?
            { name: match[1], value: match[2] } : { name: match[2], value: match[1] };
    }

    function fetchDailyCheckin(options) {
        return fetch('/daily_checkin', Object.assign({
            // 与参考脚本一致,显式携带当前登录会话的 Cookie。
            credentials: 'include',
            headers: { 'X-Requested-With': 'XMLHttpRequest' }
        }, options || {}));
    }

    function performAutoCheckin() {
        var today = getAutoCheckinDate();
        if (!settings.autoCheckin || autoCheckinInFlight || settings.autoCheckinLastDate === today) {
            return;
        }

        autoCheckinInFlight = true;
        fetchDailyCheckin({ method: 'GET' }).then(function (response) {
            if (!response.ok || /\/login(?:[?#]|$)/.test(response.url || '')) {
                throw new Error('未检测到登录状态');
            }
            return response.text();
        }).then(function (html) {
            if (isDailyCheckinLoginPage(html)) {
                throw new Error('未检测到登录状态');
            }
            if (hasCompletedDailyCheckin(html)) {
                return { completed: true };
            }

            if (!settings.autoCheckin) {
                return { cancelled: true };
            }

            var csrf = extractDailyCheckinCsrf(html);
            if (!csrf || !csrf.name || !csrf.value) {
                throw new Error('未找到签到凭据');
            }

            var formData = new FormData();
            formData.append(csrf.name, csrf.value);
            return fetchDailyCheckin({ method: 'POST', body: formData }).then(function (response) {
                if (!response.ok || /\/login(?:[?#]|$)/.test(response.url || '')) {
                    throw new Error('签到请求未获授权');
                }
                return response.text();
            }).then(function (resultHtml) {
                if (isDailyCheckinLoginPage(resultHtml)) {
                    throw new Error('签到请求未获授权');
                }
                if (hasCompletedDailyCheckin(resultHtml)) {
                    return { completed: true };
                }

                // 与参考脚本相同:POST 后再次读取状态;不因通用页面文案提前中止。
                return fetchDailyCheckin({ method: 'GET' }).then(function (verifyResponse) {
                    if (!verifyResponse.ok || /\/login(?:[?#]|$)/.test(verifyResponse.url || '')) {
                        throw new Error('无法验证签到结果');
                    }
                    return verifyResponse.text();
                }).then(function (verifyHtml) {
                    if (isDailyCheckinLoginPage(verifyHtml)) {
                        throw new Error('无法验证登录状态');
                    }
                    if (hasCompletedDailyCheckin(verifyHtml)) {
                        return { completed: true };
                    }
                    if (hasDailyCheckinFailure(verifyHtml)) {
                        throw new Error('签到状态验证失败');
                    }
                    // 参考脚本同样将成功的 POST 作为最终回退判断;兼容不显示固定成功文字的站内版本。
                    return { completed: true };
                });
            });
        }).then(function (result) {
            if (!result || !result.completed) {
                return;
            }
            settings.autoCheckinLastDate = today;
            persistSettings();
            showStatus('今日自动签到已完成');
        }).catch(function (error) {
            if (settings.autoCheckin) {
                console.warn('[LINUX.SB 自动签到]', error && error.message ? error.message : error);
                showStatus('自动签到未完成,请检查登录状态');
            }
        }).finally(function () {
            autoCheckinInFlight = false;
        });
    }

    function applyImageLightbox() {
        ensureImageLightboxEvents();
        updateImageLightboxTargets();

        if (!settings.imageLightbox) {
            closeImageLightbox();
        }
    }

    function updateImageLightboxTargets() {
        if (!document.querySelectorAll) {
            return;
        }

        document.querySelectorAll('.post-entry .post-content img, .post-item .post-content img').forEach(function (image) {
            if (settings.imageLightbox && image.getAttribute('src')) {
                image.setAttribute('data-lsb-lightbox-image', '1');
            } else {
                image.removeAttribute('data-lsb-lightbox-image');
            }
        });
    }

    function ensureImageLightboxEvents() {
        if (imageLightboxEventsBound) {
            return;
        }

        imageLightboxEventsBound = true;
        document.addEventListener('click', function (event) {
            if (!settings.imageLightbox || event.defaultPrevented) {
                return;
            }

            var target = event.target;
            if (!target || !target.matches || !target.matches('.post-entry .post-content img, .post-item .post-content img')) {
                return;
            }

            var source = target.currentSrc || target.src;
            if (!source || target.closest('#lsb-image-lightbox')) {
                return;
            }

            event.preventDefault();
            event.stopPropagation();
            openImageLightbox(source, target.alt || '帖子图片');
        }, true);

        document.addEventListener('keydown', function (event) {
            if (event.key === 'Escape' && imageLightbox && !imageLightbox.hidden) {
                closeImageLightbox();
            }
        });
    }

    function ensureImageLightbox() {
        if (imageLightbox && document.body && document.body.contains(imageLightbox)) {
            return imageLightbox;
        }
        if (!document.body) {
            return null;
        }

        imageLightbox = document.createElement('div');
        imageLightbox.id = 'lsb-image-lightbox';
        imageLightbox.hidden = true;
        imageLightbox.setAttribute('role', 'dialog');
        imageLightbox.setAttribute('aria-modal', 'true');
        imageLightbox.setAttribute('aria-label', '图片预览');

        var closeButton = document.createElement('button');
        closeButton.type = 'button';
        closeButton.className = 'lsb-lightbox-close';
        closeButton.setAttribute('aria-label', '关闭图片预览');
        closeButton.textContent = '×';

        imageLightboxImage = document.createElement('img');
        imageLightboxImage.className = 'lsb-lightbox-image';
        imageLightboxImage.alt = '';

        closeButton.addEventListener('click', closeImageLightbox);
        imageLightbox.addEventListener('click', function (event) {
            if (event.target === imageLightbox) {
                closeImageLightbox();
            }
        });

        imageLightbox.appendChild(closeButton);
        imageLightbox.appendChild(imageLightboxImage);
        document.body.appendChild(imageLightbox);
        return imageLightbox;
    }

    function openImageLightbox(source, altText) {
        var overlay = ensureImageLightbox();
        if (!overlay || !imageLightboxImage) {
            return;
        }

        imageLightboxImage.src = source;
        imageLightboxImage.alt = altText || '帖子图片';
        overlay.hidden = false;
        document.body.classList.add('lsb-lightbox-open');
        window.requestAnimationFrame(function () {
            var closeButton = overlay.querySelector('.lsb-lightbox-close');
            if (closeButton) {
                closeButton.focus();
            }
        });
    }

    function closeImageLightbox() {
        if (!imageLightbox || imageLightbox.hidden) {
            return;
        }

        imageLightbox.hidden = true;
        document.body.classList.remove('lsb-lightbox-open');
        if (imageLightboxImage) {
            imageLightboxImage.removeAttribute('src');
            imageLightboxImage.alt = '';
        }
    }

    function applyTheme() {
        clearThemeVariables();

        var theme = THEMES[settings.theme] || THEMES.neutral;
        if (theme.vars) {
            Object.keys(theme.vars).forEach(function (name) {
                setRootVariable(name, theme.vars[name]);
            });
        }

        var accentRgb = hexToRgb(settings.accent);
        setRootVariable('--brand', settings.accent);
        setRootVariable('--brand-hover', mixHex(settings.accent, '#ffffff', 0.18));
        setRootVariable('--brand-soft', 'rgba(' + accentRgb.r + ',' + accentRgb.g + ',' + accentRgb.b + ',.16)');
        setRootVariable('--focus-ring', 'rgba(' + accentRgb.r + ',' + accentRgb.g + ',' + accentRgb.b + ',.34)');

        var background = theme.vars && theme.vars['--bg'] ? theme.vars['--bg'] : '#1a1b2e';
        setRootVariable('--text', settings.textColor);
        setRootVariable('--text-muted', mixHex(settings.textColor, background, 0.34));
        setRootVariable('--text-subtle', mixHex(settings.textColor, background, 0.55));
        setRootVariable('--text-disabled', mixHex(settings.textColor, background, 0.72));

        if (settings.theme !== 'original') {
            setRootVariable('--swal2-background', 'var(--panel)');
            setRootVariable('--swal2-color', 'var(--text)');
            setRootVariable('--swal2-validation-message-background', 'var(--line-soft)');
            setRootVariable('--swal2-validation-message-color', 'var(--text-muted)');
        }
    }

    function hexToRgb(hex) {
        var normalized = String(hex).replace('#', '');
        return {
            r: parseInt(normalized.slice(0, 2), 16),
            g: parseInt(normalized.slice(2, 4), 16),
            b: parseInt(normalized.slice(4, 6), 16)
        };
    }

    function mixHex(first, second, weight) {
        var a = hexToRgb(first);
        var b = hexToRgb(second);
        var mix = function (left, right) {
            return Math.round(left + (right - left) * weight);
        };
        var toHex = function (number) {
            return number.toString(16).padStart(2, '0');
        };
        return '#' + toHex(mix(a.r, b.r)) + toHex(mix(a.g, b.g)) + toHex(mix(a.b, b.b));
    }

    function findTextPalette(color) {
        var normalized = String(color).toLowerCase();
        var key = Object.keys(TEXT_PALETTES).find(function (paletteKey) {
            return paletteKey !== 'custom' && TEXT_PALETTES[paletteKey].color === normalized;
        });
        return key || 'custom';
    }

    function ensureInterface() {
        if (panel && document.body.contains(panel)) {
            return;
        }
        if (!document.body) {
            return;
        }

        toggleButton = document.createElement('button');
        toggleButton.id = 'lsb-layout-toggle';
        toggleButton.type = 'button';
        toggleButton.setAttribute('aria-label', '打开布局与主题设置');
        toggleButton.setAttribute('aria-controls', 'lsb-layout-panel');
        toggleButton.setAttribute('aria-expanded', 'false');
        toggleButton.title = '布局与主题设置';
        toggleButton.innerHTML = [
            '<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">',
            '<path d="M4 7h10M18 7h2M4 17h2M10 17h10M14 4v6M7 14v6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>',
            '</svg>'
        ].join('');

        panel = document.createElement('section');
        panel.id = 'lsb-layout-panel';
        panel.hidden = true;
        panel.setAttribute('role', 'dialog');
        panel.setAttribute('aria-modal', 'false');
        panel.setAttribute('aria-label', '布局与主题设置');
        panel.innerHTML = [
            '<div class="lsb-panel-head">',
            '  <button class="lsb-icon-button" type="button" data-lsb-close aria-label="关闭设置面板">×</button>',
            '  <span class="lsb-panel-credit">Design 1 @nmxyh | Design 2 <a href="https://com.com.ee" target="_blank" rel="noopener noreferrer">COMCOM</a></span>',
            '</div>',
            '<div class="lsb-panel-body">',
            '  <section class="lsb-section" aria-labelledby="lsb-layout-title">',
            '    <h2 class="lsb-section-title" id="lsb-layout-title">布局参数</h2>',
            '    <div data-lsb-ranges></div>',
            '  </section>',
            '  <section class="lsb-section" aria-labelledby="lsb-home-title">',
            '    <h2 class="lsb-section-title" id="lsb-home-title">首页设置</h2>',
            '    <label class="lsb-check-line"><input type="checkbox" data-lsb-home-personalized><span>启用首页个性化头图与搜索</span></label>',
            '    <label class="lsb-check-line"><input type="checkbox" data-lsb-home-sidebar-swap><span>侧栏位置对调</span></label>',
            '    <label class="lsb-check-line"><input type="checkbox" data-lsb-auto-checkin><span>自动签到功能</span></label>',
            '    <label class="lsb-check-line"><input type="checkbox" data-lsb-image-lightbox><span>图片灯箱功能</span></label>',
            '    <div class="lsb-check-line-row">',
            '      <label class="lsb-check-line"><input type="checkbox" data-lsb-image-upload><span>拖拽图片上传</span></label>',
            '      <button class="lsb-upload-settings-toggle" type="button" data-lsb-upload-settings-toggle aria-expanded="false">展开图床配置</button>',
            '    </div>',
            '    <div class="lsb-upload-settings" data-lsb-upload-settings>',
            '      <label class="lsb-field"><span>图床类型</span><select class="lsb-select" data-lsb-upload-provider><option value="imgur">Imgur(默认)</option><option value="nodeimage">Nodeimage</option><option value="custom">自定义接口</option></select></label>',
            '      <label class="lsb-field"><span>图床主页</span><input class="lsb-input" type="url" inputmode="url" data-lsb-upload-host placeholder="https://imgur.com/"></label>',
            '      <label class="lsb-field"><span>上传接口(HTTPS)</span><input class="lsb-input" type="url" inputmode="url" data-lsb-upload-endpoint placeholder="https://api.imgur.com/3/image"></label>',
            '      <label class="lsb-field"><span>文件字段名</span><input class="lsb-input" type="text" data-lsb-upload-file-field placeholder="image"></label>',
            '      <label class="lsb-field"><span>返回图片地址字段</span><input class="lsb-input" type="text" data-lsb-upload-response-path placeholder="data.link"></label>',
            '      <label class="lsb-field"><span>认证方式</span><select class="lsb-select" data-lsb-upload-auth-mode><option value="imgur-client-id">Imgur Client ID</option><option value="nodeimage-api-key">Nodeimage API Key</option><option value="none">不使用认证</option><option value="bearer">Bearer Token</option></select></label>',
            '      <label class="lsb-field"><span>API 密钥 / Token</span><input class="lsb-input" type="password" autocomplete="off" data-lsb-upload-token placeholder="Imgur 需填写 Client ID"></label>',
            '      <p class="lsb-upload-hint">启用后可将图片拖入发帖或回复编辑器,或点击编辑器下方的“上传图片”。默认使用 Imgur;其匿名上传需要填写 Client ID。自定义接口应返回可访问的 HTTPS 图片直链。</p>',
            '    </div>',
            '    <div class="lsb-filter-section">',
            '      <label class="lsb-field"><span>过滤标题关键字(每行一个,最多 10 个)</span>',
            '        <textarea class="lsb-textarea" data-lsb-title-filters rows="3" placeholder="输入要屏蔽的关键字,每行一个"></textarea>',
            '      </label>',
            '      <label class="lsb-field"><span>过滤用户名(每行一个,最多 10 个)</span>',
            '        <textarea class="lsb-textarea" data-lsb-user-filters rows="3" placeholder="输入要屏蔽的用户名,每行一个"></textarea>',
            '      </label>',
            '    </div>',
            '  </section>',
            '  <section class="lsb-section" aria-labelledby="lsb-theme-title">',
            '    <h2 class="lsb-section-title" id="lsb-theme-title">主题颜色</h2>',
            '    <label class="lsb-field"><span>配色方案</span><select class="lsb-select" data-lsb-theme></select></label>',
        '    <label class="lsb-field"><span>强调色</span>',
            '      <span class="lsb-color-line">',
            '        <input class="lsb-color-input" type="color" data-lsb-accent aria-label="选择强调色">',
            '        <output class="lsb-color-value" data-lsb-accent-value></output>',
            '      </span>',
            '    </label>',
            '    <label class="lsb-field"><span>文字色板</span><select class="lsb-select" data-lsb-text-palette></select></label>',
            '    <label class="lsb-field"><span>文字颜色</span>',
            '      <span class="lsb-color-line">',
            '        <input class="lsb-color-input" type="color" data-lsb-text-color aria-label="选择文字颜色">',
            '        <output class="lsb-color-value" data-lsb-text-color-value></output>',
            '      </span>',
            '    </label>',
            '    <p class="lsb-theme-note">中性深灰使用纯灰阶构建背景层级;强调色负责交互状态,文字色负责内容层级。</p>',
            '  </section>',
            '  <div class="lsb-actions">',
            '    <button class="lsb-button" type="button" data-lsb-reset>恢复默认</button>',
            '    <button class="lsb-button lsb-button-primary" type="button" data-lsb-done>完成</button>',
            '  </div>',
            '  <p class="lsb-status" data-lsb-status aria-live="polite"></p>',
            '</div>'
        ].join('');

        document.body.appendChild(toggleButton);
        document.body.appendChild(panel);
        // 初始设置侧栏位置类
        toggleButton.classList.add(settings.sidebarSwap ? 'lsb-toggle-left' : 'lsb-toggle-right');
        panel.classList.add(settings.sidebarSwap ? 'lsb-panel-left' : 'lsb-panel-right');
        statusElement = panel.querySelector('[data-lsb-status]');

        buildRangeControls();
        buildThemeControls();
        bindInterfaceEvents();
        bindDragEvents();
        syncInterface();
        applySettings();
        restoreTogglePosition();

    }

    function isHomePage() {
        var path = window.location.pathname.replace(/\/+$/, '') || '/';
        if (path === '/') {
            return true;
        }
        if (path !== '/index.php') {
            return false;
        }
        var query = new URLSearchParams(window.location.search);
        return !query.has('a') && !query.has('do') && !query.has('id');
    }

    function applyHomePersonalization() {
        var existing = document.getElementById('lsb-home-personalization');
        if (!settings.homePersonalized || !isHomePage()) {
            if (existing) {
                existing.remove();
            }
            document.querySelectorAll('.lsb-home-personalized-layout').forEach(function (layout) {
                layout.classList.remove('lsb-home-personalized-layout');
            });
            return;
        }

        var forumLayout = document.querySelector('main.wrap .home-shell .forum-layout, main.wrap .forum-layout');
        var forumMain = forumLayout ? forumLayout.querySelector('.forum-main') : null;
        if (!forumLayout || !forumMain) {
            return;
        }

        if (existing) {
            if (existing.parentElement !== forumLayout) {
                forumLayout.insertBefore(existing, forumLayout.firstChild);
            }
            forumLayout.classList.add('lsb-home-personalized-layout');
            return;
        }

        var hero = document.createElement('section');
        hero.id = 'lsb-home-personalization';
        hero.setAttribute('aria-label', 'LINUX SB 首页欢迎区');

        var logo = document.createElement('div');
        logo.className = 'lsb-home-logo';
        logo.innerHTML = HOME_LOGO_SVG;

        var tagline = document.createElement('h1');
        tagline.className = 'lsb-home-tagline';
        tagline.textContent = 'Here IS The New Ideal Community';

        var sourceSearch = document.querySelector('.top .search-form, .search-form');
        if (!sourceSearch) {
            return;
        }
        var search = sourceSearch.cloneNode(true);
        stripSearchEnhancement(search);
        search.className = 'search-form lsb-home-search-form';
        search.removeAttribute('id');
        search.setAttribute('aria-label', '首页搜索');
        var searchInput = search.querySelector('.search-input');
        if (searchInput) {
            searchInput.setAttribute('aria-label', '搜索关键词');
        }

        hero.appendChild(logo);
        hero.appendChild(tagline);
        hero.appendChild(search);

        forumLayout.insertBefore(hero, forumLayout.firstChild);
        forumLayout.classList.add('lsb-home-personalized-layout');
    }

    function applySidebarSwap() {
        if (!isHomePage()) {
            return;
        }
        var forumLayout = document.querySelector('main.wrap .home-shell .forum-layout, main.wrap .forum-layout');
        if (forumLayout) {
            if (settings.sidebarSwap) {
                forumLayout.classList.add('lsb-home-sidebar-swapped');
            } else {
                forumLayout.classList.remove('lsb-home-sidebar-swapped');
            }
        }

        // 切换按钮与面板位置对调联动
        if (toggleButton) {
            if (settings.sidebarSwap) {
                toggleButton.classList.add('lsb-toggle-left');
                toggleButton.classList.remove('lsb-toggle-right');
            } else {
                toggleButton.classList.add('lsb-toggle-right');
                toggleButton.classList.remove('lsb-toggle-left');
            }
        }
        if (panel) {
            if (settings.sidebarSwap) {
                panel.classList.add('lsb-panel-left');
                panel.classList.remove('lsb-panel-right');
            } else {
                panel.classList.add('lsb-panel-right');
                panel.classList.remove('lsb-panel-left');
            }
        }
    }

    var filterDebounceTimer = 0;

    function scheduleFilter() {
        window.clearTimeout(filterDebounceTimer);
        filterDebounceTimer = window.setTimeout(applyFilters, 200);
    }

    function applyFilters() {
        var titleKeywords = (settings.titleFilters || []).filter(function(k) { return k.trim(); });
        var usernames = (settings.userFilters || []).filter(function(u) { return u.trim(); });

        var hasFilters = titleKeywords.length > 0 || usernames.length > 0;

        if (!hasFilters) {
            document.querySelectorAll('.post-item[data-lsb-filtered]').forEach(function(item) {
                item.style.display = '';
                item.removeAttribute('data-lsb-filtered');
            });
            return;
        }

        document.querySelectorAll('.post-item').forEach(function(item) {
            var shouldHide = false;

            // 标题关键字过滤
            if (!shouldHide && titleKeywords.length > 0) {
                var titleEl = item.querySelector('.post-title');
                if (titleEl) {
                    var title = titleEl.textContent.toLowerCase();
                    shouldHide = titleKeywords.some(function(kw) {
                        return title.includes(kw.toLowerCase());
                    });
                }
            }

            // 用户名过滤 — 多选择器回退,兼容首页列表与帖子详情页
            if (!shouldHide && usernames.length > 0) {
                var matchedAuthor = '';
                // 选择器1:帖子详情页评论作者
                var authorEl = item.querySelector('.post-author');
                // 选择器2:用户资料链接(首页列表通用)
                if (!authorEl) {
                    authorEl = item.querySelector('.post-meta a[href*="/user/"]');
                }
                // 选择器3:回退到 .post-meta 内第一个链接
                if (!authorEl) {
                    var meta = item.querySelector('.post-meta');
                    if (meta) {
                        authorEl = meta.querySelector('a');
                    }
                }
                if (authorEl) {
                    matchedAuthor = authorEl.textContent.trim();
                }

                if (matchedAuthor) {
                    var lower = matchedAuthor.toLowerCase();
                    shouldHide = usernames.some(function(u) {
                        return lower === u.toLowerCase();
                    });
                }
            }

            if (shouldHide) {
                item.style.display = 'none';
                item.setAttribute('data-lsb-filtered', '1');
            } else {
                item.style.display = '';
                item.removeAttribute('data-lsb-filtered');
            }
        });
    }

    function enforceRadiusOverrides() {
        var tabRadius = Math.min(settings.radius, 12) + 'px';
        var searchRadius = Math.min(settings.radius, 12) + 'px';

        document.querySelectorAll('.topic-toolbar > .tab-bar').forEach(function (tabBar) {
            tabBar.style.setProperty('display', 'inline-flex', 'important');
            tabBar.style.setProperty('flex', '0 0 auto', 'important');
            tabBar.style.setProperty('gap', '5px', 'important');
            tabBar.style.setProperty('width', 'max-content', 'important');
            tabBar.style.setProperty('border-radius', '0px', 'important');
            tabBar.style.setProperty('overflow', 'visible', 'important');
            tabBar.style.setProperty('isolation', 'auto', 'important');
            tabBar.style.setProperty('transform', 'none', 'important');

            tabBar.querySelectorAll(':scope > a.tab').forEach(function (tab) {
                tab.style.setProperty('display', 'inline-flex', 'important');
                tab.style.setProperty('align-items', 'center', 'important');
                tab.style.setProperty('justify-content', 'center', 'important');
                tab.style.setProperty('min-height', '27px', 'important');
                tab.style.setProperty('line-height', '1.1', 'important');
                tab.style.setProperty('box-sizing', 'border-box', 'important');
                tab.style.setProperty('border-radius', tabRadius, 'important');
                tab.style.setProperty('border-top-left-radius', tabRadius, 'important');
                tab.style.setProperty('border-top-right-radius', tabRadius, 'important');
                tab.style.setProperty('border-bottom-right-radius', tabRadius, 'important');
                tab.style.setProperty('border-bottom-left-radius', tabRadius, 'important');
                tab.style.setProperty('margin-left', '0px', 'important');
                tab.style.setProperty('overflow', 'hidden', 'important');
                tab.style.setProperty('background-clip', 'padding-box', 'important');
            });
        });

        document.querySelectorAll('.search-form').forEach(function (form) {
            form.style.setProperty('border-radius', searchRadius, 'important');
        });
        document.querySelectorAll('.search-field').forEach(function (field) {
            field.style.setProperty('border-radius', searchRadius, 'important');
        });
        document.querySelectorAll('.lsb-search-select-trigger').forEach(function (trigger) {
            trigger.style.setProperty('border-top-left-radius', searchRadius, 'important');
            trigger.style.setProperty('border-bottom-left-radius', searchRadius, 'important');
        });
        document.querySelectorAll('.search-btn').forEach(function (button) {
            button.style.setProperty('border-top-right-radius', searchRadius, 'important');
            button.style.setProperty('border-bottom-right-radius', searchRadius, 'important');
        });
    }

    function stripSearchEnhancement(form) {
        form.querySelectorAll('.lsb-search-select').forEach(function (wrapper) {
            var select = wrapper.querySelector('.search-field');
            if (select) {
                select.classList.remove('lsb-search-native');
                select.removeAttribute('data-lsb-search-enhanced');
                wrapper.replaceWith(select);
            }
        });
    }

    function enhanceSearchFields(root) {
        if (!root || !root.querySelectorAll) {
            return;
        }

        root.querySelectorAll('.search-field').forEach(function (select) {
            if (select.getAttribute('data-lsb-search-enhanced') === '1') {
                return;
            }

            select.setAttribute('data-lsb-search-enhanced', '1');
            var wrapper = document.createElement('div');
            wrapper.className = 'lsb-search-select';

            var trigger = document.createElement('button');
            trigger.type = 'button';
            trigger.className = 'lsb-search-select-trigger';
            trigger.setAttribute('aria-haspopup', 'listbox');
            trigger.setAttribute('aria-expanded', 'false');
            trigger.setAttribute('aria-label', '选择搜索范围');

            var menu = document.createElement('div');
            menu.className = 'lsb-search-options';
            menu.setAttribute('role', 'listbox');
            menu.hidden = true;
            menu.id = 'lsb-search-options-' + (++searchDropdownId);
            trigger.setAttribute('aria-controls', menu.id);

            Array.from(select.options).forEach(function (option) {
                var item = document.createElement('button');
                item.type = 'button';
                item.className = 'lsb-search-option';
                item.setAttribute('role', 'option');
                item.dataset.value = option.value;
                item.textContent = option.textContent;
                item.addEventListener('click', function () {
                    select.value = option.value;
                    select.dispatchEvent(new Event('change', { bubbles: true }));
                    closeSearchDropdowns();
                    trigger.focus();
                });
                menu.appendChild(item);
            });

            var sync = function () {
                var selected = select.options[select.selectedIndex] || select.options[0];
                trigger.textContent = selected ? selected.textContent : '';
                menu.querySelectorAll('.lsb-search-option').forEach(function (item) {
                    var active = selected && item.dataset.value === selected.value;
                    item.setAttribute('aria-selected', String(Boolean(active)));
                });
            };

            trigger.addEventListener('click', function () {
                var nextOpen = menu.hidden;
                closeSearchDropdowns();
                menu.hidden = !nextOpen;
                trigger.setAttribute('aria-expanded', String(nextOpen));
            });
            trigger.addEventListener('keydown', function (event) {
                if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {
                    event.preventDefault();
                    trigger.click();
                }
                if (event.key === 'Escape') {
                    closeSearchDropdowns();
                }
            });
            select.addEventListener('change', sync);

            wrapper.appendChild(trigger);
            wrapper.appendChild(menu);
            select.classList.add('lsb-search-native');
            select.parentNode.insertBefore(wrapper, select);
            wrapper.appendChild(select);
            sync();
        });

        if (!searchDropdownEventsBound) {
            searchDropdownEventsBound = true;
            document.addEventListener('pointerdown', function (event) {
                if (!event.target.closest || !event.target.closest('.lsb-search-select')) {
                    closeSearchDropdowns();
                }
            });
            document.addEventListener('keydown', function (event) {
                if (event.key === 'Escape') {
                    closeSearchDropdowns();
                }
            });
        }
    }

    function closeSearchDropdowns() {
        document.querySelectorAll('.lsb-search-options').forEach(function (menu) {
            menu.hidden = true;
            var trigger = menu.parentElement && menu.parentElement.querySelector('.lsb-search-select-trigger');
            if (trigger) {
                trigger.setAttribute('aria-expanded', 'false');
            }
        });
    }

    function buildRangeControls() {
        var container = panel.querySelector('[data-lsb-ranges]');
        container.className = 'lsb-section';

        RANGE_DEFINITIONS.forEach(function (definition) {
            var label = document.createElement('label');
            label.className = 'lsb-range-row';

            var head = document.createElement('span');
            head.className = 'lsb-range-head';

            var name = document.createElement('span');
            name.className = 'lsb-range-label';
            name.textContent = definition.label;

            var output = document.createElement('output');
            output.className = 'lsb-range-value';
            output.dataset.valueFor = definition.key;

            var input = document.createElement('input');
            input.className = 'lsb-range';
            input.type = 'range';
            input.min = String(definition.min);
            input.max = String(definition.max);
            input.step = String(definition.step);
            input.dataset.settingKey = definition.key;
            input.setAttribute('aria-label', definition.label);

            head.appendChild(name);
            head.appendChild(output);
            label.appendChild(head);
            label.appendChild(input);
            container.appendChild(label);

            input.addEventListener('input', function () {
                settings[definition.key] = Number(input.value);
                output.value = input.value + definition.unit;
                output.textContent = output.value;
                applySettings();
                scheduleSave();
            });
        });
    }

    function buildThemeControls() {
        var select = panel.querySelector('[data-lsb-theme]');
        Object.keys(THEMES).forEach(function (key) {
            var option = document.createElement('option');
            option.value = key;
            option.textContent = THEMES[key].label;
            select.appendChild(option);
        });

        select.addEventListener('change', function () {
            settings.theme = select.value;
            settings.accent = THEMES[settings.theme].accent;
            settings.textColor = THEMES[settings.theme].textColor || DEFAULTS.textColor;
            settings.textPalette = findTextPalette(settings.textColor);
            applySettings();
            syncInterface();
            persistSettings();
        });

        var accentInput = panel.querySelector('[data-lsb-accent]');
        accentInput.addEventListener('input', function () {
            settings.accent = accentInput.value.toLowerCase();
            applySettings();
            syncAccentControl();
            scheduleSave();
        });

        var textPalette = panel.querySelector('[data-lsb-text-palette]');
        Object.keys(TEXT_PALETTES).forEach(function (key) {
            var option = document.createElement('option');
            option.value = key;
            option.textContent = TEXT_PALETTES[key].label;
            textPalette.appendChild(option);
        });

        textPalette.addEventListener('change', function () {
            settings.textPalette = textPalette.value;
            if (settings.textPalette !== 'custom') {
                settings.textColor = TEXT_PALETTES[settings.textPalette].color;
            }
            applySettings();
            syncTextControl();
            persistSettings();
        });

        var textInput = panel.querySelector('[data-lsb-text-color]');
        textInput.addEventListener('input', function () {
            settings.textPalette = 'custom';
            settings.textColor = textInput.value.toLowerCase();
            applySettings();
            syncTextControl();
            scheduleSave();
        });
    }

    function bindInterfaceEvents() {
        panel.querySelector('[data-lsb-home-personalized]').addEventListener('change', function (event) {
            settings.homePersonalized = event.target.checked;
            applySettings();
            syncInterface();
            persistSettings();
        });

        panel.querySelector('[data-lsb-home-sidebar-swap]').addEventListener('change', function (event) {
            settings.sidebarSwap = event.target.checked;
            applySidebarSwap();
            syncInterface();
            persistSettings();
        });

        panel.querySelector('[data-lsb-auto-checkin]').addEventListener('change', function (event) {
            settings.autoCheckin = event.target.checked;
            applyAutoCheckin();
            syncInterface();
            persistSettings();
        });

        panel.querySelector('[data-lsb-image-lightbox]').addEventListener('change', function (event) {
            settings.imageLightbox = event.target.checked;
            applyImageLightbox();
            syncInterface();
            persistSettings();
        });

        panel.querySelector('[data-lsb-image-upload]').addEventListener('change', function (event) {
            settings.imageUpload = event.target.checked;
            applyImageUpload();
            syncInterface();
            persistSettings();
        });

        panel.querySelector('[data-lsb-upload-settings-toggle]').addEventListener('click', function () {
            settings.imageUploadSettingsCollapsed = !settings.imageUploadSettingsCollapsed;
            syncImageUploadControls();
            persistSettings();
        });

        panel.querySelector('[data-lsb-upload-provider]').addEventListener('change', function (event) {
            settings.imageUploadProvider = event.target.value;
            if (settings.imageUploadProvider === 'imgur') {
                settings.imageUploadHost = DEFAULTS.imageUploadHost;
                settings.imageUploadEndpoint = DEFAULTS.imageUploadEndpoint;
                settings.imageUploadFileField = DEFAULTS.imageUploadFileField;
                settings.imageUploadResponsePath = DEFAULTS.imageUploadResponsePath;
                settings.imageUploadAuthMode = DEFAULTS.imageUploadAuthMode;
            } else if (settings.imageUploadProvider === 'nodeimage') {
                settings.imageUploadHost = NODEIMAGE_PRESET.host;
                settings.imageUploadEndpoint = NODEIMAGE_PRESET.endpoint;
                settings.imageUploadFileField = NODEIMAGE_PRESET.field;
                settings.imageUploadResponsePath = NODEIMAGE_PRESET.responsePath;
                settings.imageUploadAuthMode = NODEIMAGE_PRESET.authMode;
                settings.imageUploadToken = NODEIMAGE_PRESET.token;
            }
            syncInterface();
            persistSettings();
        });

        [
            ['[data-lsb-upload-host]', 'imageUploadHost'],
            ['[data-lsb-upload-endpoint]', 'imageUploadEndpoint'],
            ['[data-lsb-upload-file-field]', 'imageUploadFileField'],
            ['[data-lsb-upload-response-path]', 'imageUploadResponsePath'],
            ['[data-lsb-upload-auth-mode]', 'imageUploadAuthMode'],
            ['[data-lsb-upload-token]', 'imageUploadToken']
        ].forEach(function (item) {
            var control = panel.querySelector(item[0]);
            control.addEventListener('change', function () {
                settings[item[1]] = control.value.trim();
                syncImageUploadControls();
                persistSettings();
            });
        });

        var parseFilterTextarea = function (value, maxItems) {
            var items = String(value).split(/[\n,]+/).map(function(s) { return s.trim(); }).filter(Boolean);
            if (maxItems > 0) {
                items = items.slice(0, maxItems);
            }
            return items;
        };

        var titleFiltersTextarea = panel.querySelector('[data-lsb-title-filters]');
        titleFiltersTextarea.addEventListener('blur', function () {
            var value = titleFiltersTextarea.value;
            settings.titleFilters = parseFilterTextarea(value, 10);
            titleFiltersTextarea.value = settings.titleFilters.join('\n');
            applyFilters();
            persistSettings();
        });

        var userFiltersTextarea = panel.querySelector('[data-lsb-user-filters]');
        userFiltersTextarea.addEventListener('blur', function () {
            var value = userFiltersTextarea.value;
            settings.userFilters = parseFilterTextarea(value, 10);
            userFiltersTextarea.value = settings.userFilters.join('\n');
            applyFilters();
            persistSettings();
        });

        toggleButton.addEventListener('click', function () {
            if (suppressToggleClick) {
                suppressToggleClick = false;
                return;
            }
            setPanelOpen(panel.hidden);
        });

        panel.querySelector('[data-lsb-close]').addEventListener('click', function () {
            setPanelOpen(false);
        });

        panel.querySelector('[data-lsb-done]').addEventListener('click', function () {
            persistSettings();
            setPanelOpen(false);
        });

        panel.querySelector('[data-lsb-reset]').addEventListener('click', function () {
            var previousLeft = settings.panelLeft;
            var previousTop = settings.panelTop;
            var previousToggleLeft = settings.toggleLeft;
            var previousToggleTop = settings.toggleTop;
            settings = normalizeSettings(DEFAULTS);
            settings.panelLeft = previousLeft;
            settings.panelTop = previousTop;
            settings.toggleLeft = previousToggleLeft;
            settings.toggleTop = previousToggleTop;
            applySettings();
            syncInterface();
            persistSettings();
            showStatus('已恢复默认中性深灰方案');
        });

        document.addEventListener('keydown', function (event) {
            if (event.key === 'Escape' && panel && !panel.hidden) {
                setPanelOpen(false);
            }
        });

        document.addEventListener('pointerdown', function (event) {
            if (!panel || panel.hidden) {
                return;
            }
            if (!panel.contains(event.target) && !toggleButton.contains(event.target)) {
                setPanelOpen(false);
            }
        });
    }

    function bindDragEvents() {
        var head = panel.querySelector('.lsb-panel-head');
        var dragState = null;

        head.addEventListener('pointerdown', function (event) {
            var target = event.target;
            if (event.button !== 0 || (target && target.closest && target.closest('button, a'))) {
                return;
            }

            var rect = panel.getBoundingClientRect();
            dragState = {
                pointerId: event.pointerId,
                offsetX: event.clientX - rect.left,
                offsetY: event.clientY - rect.top
            };
            panel.classList.add('lsb-dragging');
            if (head.setPointerCapture) {
                head.setPointerCapture(event.pointerId);
            }
            event.preventDefault();
        });

        head.addEventListener('pointermove', function (event) {
            if (!dragState || event.pointerId !== dragState.pointerId) {
                return;
            }
            setPanelPosition(
                event.clientX - dragState.offsetX,
                event.clientY - dragState.offsetY,
                false
            );
        });

        var finishDrag = function (event) {
            if (!dragState || event.pointerId !== dragState.pointerId) {
                return;
            }
            if (head.releasePointerCapture) {
                try {
                    head.releasePointerCapture(event.pointerId);
                } catch (error) {
                    // 某些浏览器在 pointercancel 后会抛出无害异常。
                }
            }
            dragState = null;
            panel.classList.remove('lsb-dragging');
            persistSettings();
        };

        head.addEventListener('pointerup', finishDrag);
        head.addEventListener('pointercancel', finishDrag);

        var toggleDragState = null;
        toggleButton.addEventListener('pointerdown', function (event) {
            if (event.button !== 0) {
                return;
            }

            var rect = toggleButton.getBoundingClientRect();
            toggleDragState = {
                pointerId: event.pointerId,
                startX: event.clientX,
                startY: event.clientY,
                offsetX: event.clientX - rect.left,
                offsetY: event.clientY - rect.top,
                moved: false
            };
            toggleButton.classList.add('lsb-toggle-dragging');
            if (toggleButton.setPointerCapture) {
                toggleButton.setPointerCapture(event.pointerId);
            }
            event.preventDefault();
        });

        toggleButton.addEventListener('pointermove', function (event) {
            if (!toggleDragState || event.pointerId !== toggleDragState.pointerId) {
                return;
            }

            if (Math.abs(event.clientX - toggleDragState.startX) > 4 ||
                Math.abs(event.clientY - toggleDragState.startY) > 4) {
                toggleDragState.moved = true;
            }

            if (toggleDragState.moved) {
                setTogglePosition(
                    event.clientX - toggleDragState.offsetX,
                    event.clientY - toggleDragState.offsetY,
                    false
                );
            }
        });

        var finishToggleDrag = function (event) {
            if (!toggleDragState || event.pointerId !== toggleDragState.pointerId) {
                return;
            }
            if (toggleButton.releasePointerCapture) {
                try {
                    toggleButton.releasePointerCapture(event.pointerId);
                } catch (error) {
                    // 某些浏览器在 pointercancel 后会抛出无害异常。
                }
            }
            var moved = toggleDragState.moved;
            suppressToggleClick = true;
            toggleDragState = null;
            toggleButton.classList.remove('lsb-toggle-dragging');
            if (moved) {
                persistSettings();
            } else if (event.type === 'pointerup') {
                setPanelOpen(panel.hidden);
            }
            window.setTimeout(function () {
                suppressToggleClick = false;
            }, 0);
        };

        toggleButton.addEventListener('pointerup', finishToggleDrag);
        toggleButton.addEventListener('pointercancel', finishToggleDrag);

        window.addEventListener('resize', function () {
            if (panel && !panel.hidden && settings.panelLeft !== null && settings.panelTop !== null) {
                setPanelPosition(settings.panelLeft, settings.panelTop, false);
                scheduleSave();
            }
            if (settings.toggleLeft !== null && settings.toggleTop !== null) {
                setTogglePosition(settings.toggleLeft, settings.toggleTop, false);
                scheduleSave();
            }
        });
    }

    function setPanelPosition(left, top, persist) {
        if (!panel) {
            return;
        }

        var margin = 8;
        var maxLeft = Math.max(margin, window.innerWidth - panel.offsetWidth - margin);
        var maxTop = Math.max(margin, window.innerHeight - panel.offsetHeight - margin);
        var nextLeft = Math.round(Math.min(maxLeft, Math.max(margin, left)));
        var nextTop = Math.round(Math.min(maxTop, Math.max(margin, top)));

        panel.style.left = nextLeft + 'px';
        panel.style.top = nextTop + 'px';
        panel.style.right = 'auto';
        panel.style.bottom = 'auto';
        settings.panelLeft = nextLeft;
        settings.panelTop = nextTop;

        if (persist) {
            persistSettings();
        }
    }

    function restorePanelPosition() {
        if (!panel || settings.panelLeft === null || settings.panelTop === null) {
            return;
        }
        setPanelPosition(settings.panelLeft, settings.panelTop, false);
    }

    function setTogglePosition(left, top, persist) {
        if (!toggleButton) {
            return;
        }

        var margin = 8;
        var maxLeft = Math.max(margin, window.innerWidth - toggleButton.offsetWidth - margin);
        var maxTop = Math.max(margin, window.innerHeight - toggleButton.offsetHeight - margin);
        var nextLeft = Math.round(Math.min(maxLeft, Math.max(margin, left)));
        var nextTop = Math.round(Math.min(maxTop, Math.max(margin, top)));

        toggleButton.style.left = nextLeft + 'px';
        toggleButton.style.top = nextTop + 'px';
        toggleButton.style.right = 'auto';
        toggleButton.style.bottom = 'auto';
        settings.toggleLeft = nextLeft;
        settings.toggleTop = nextTop;

        if (persist) {
            persistSettings();
        }
    }

    function restoreTogglePosition() {
        if (!toggleButton || settings.toggleLeft === null || settings.toggleTop === null) {
            return;
        }
        setTogglePosition(settings.toggleLeft, settings.toggleTop, false);
    }

    function syncInterface() {
        if (!panel) {
            return;
        }

        RANGE_DEFINITIONS.forEach(function (definition) {
            var input = panel.querySelector('[data-setting-key="' + definition.key + '"]');
            var output = panel.querySelector('[data-value-for="' + definition.key + '"]');
            input.value = String(settings[definition.key]);
            output.value = settings[definition.key] + definition.unit;
            output.textContent = output.value;
        });

        panel.querySelector('[data-lsb-theme]').value = settings.theme;
        panel.querySelector('[data-lsb-home-personalized]').checked = settings.homePersonalized;
        panel.querySelector('[data-lsb-home-sidebar-swap]').checked = settings.sidebarSwap;
        panel.querySelector('[data-lsb-auto-checkin]').checked = settings.autoCheckin;
        panel.querySelector('[data-lsb-image-lightbox]').checked = settings.imageLightbox;
        panel.querySelector('[data-lsb-image-upload]').checked = settings.imageUpload;
        syncImageUploadControls();
        var titleFiltersTextarea = panel.querySelector('[data-lsb-title-filters]');
        if (titleFiltersTextarea) {
            titleFiltersTextarea.value = (settings.titleFilters || []).join('\n');
        }
        var userFiltersTextarea = panel.querySelector('[data-lsb-user-filters]');
        if (userFiltersTextarea) {
            userFiltersTextarea.value = (settings.userFilters || []).join('\n');
        }
        syncAccentControl();
        syncTextControl();
    }

    function syncAccentControl() {
        if (!panel) {
            return;
        }
        panel.querySelector('[data-lsb-accent]').value = settings.accent;
        panel.querySelector('[data-lsb-accent-value]').value = settings.accent.toUpperCase();
        panel.querySelector('[data-lsb-accent-value]').textContent = settings.accent.toUpperCase();
        panel.style.setProperty('--lsb-ui-accent', settings.accent);
        toggleButton.style.setProperty('--lsb-ui-accent', settings.accent);
    }

    function syncTextControl() {
        if (!panel) {
            return;
        }
        panel.querySelector('[data-lsb-text-palette]').value = settings.textPalette;
        panel.querySelector('[data-lsb-text-color]').value = settings.textColor;
        panel.querySelector('[data-lsb-text-color-value]').value = settings.textColor.toUpperCase();
        panel.querySelector('[data-lsb-text-color-value]').textContent = settings.textColor.toUpperCase();
        panel.style.setProperty('--lsb-ui-text', settings.textColor);
        toggleButton.style.setProperty('--lsb-ui-text', settings.textColor);
    }

    function setPanelOpen(open) {
        if (!panel || !toggleButton) {
            return;
        }

        panel.hidden = !open;
        toggleButton.setAttribute('aria-expanded', String(open));
        toggleButton.setAttribute('aria-label', open ? '关闭布局与主题设置' : '打开布局与主题设置');

        if (open) {
            restorePanelPosition();
            syncInterface();
            window.requestAnimationFrame(function () {
                panel.querySelector('[data-lsb-close]').focus();
            });
        } else {
            toggleButton.focus();
        }
    }

    function showStatus(message) {
        if (!statusElement) {
            return;
        }
        statusElement.textContent = message;
        window.setTimeout(function () {
            if (statusElement && statusElement.textContent === message) {
                statusElement.textContent = '';
            }
        }, 1600);
    }
})();