LINUX.SB-Enhance-Script

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

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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);
    }
})();