[js] 思源界面主题色跟随当前题头图变化的 js 片段

思源界面主题色跟随当前题头图变化的 js 片段

目前有按钮等变化,其他变化自行添加 css 容器

/*! Fast Average Color | © 2025 Denis Seleznev | MIT License | https://github.com/fast-average-color/fast-average-color */
'use strict';
function toHex(num) {
    var str = num.toString(16);
    return str.length === 1 ? '0' + str : str;
}
function arrayToHex(arr) {
    return '#' + arr.map(toHex).join('');
}
function isDark(color) {
    // http://www.w3.org/TR/AERT#color-contrast
    var result = (color[0] * 299 + color[1] * 587 + color[2] * 114) / 1000;
    return result < 128;
}
function prepareIgnoredColor(color) {
    if (!color) {
        return [];
    }
    return isRGBArray(color) ? color : [color];
}
function isRGBArray(value) {
    return Array.isArray(value[0]);
}
function isIgnoredColor(data, index, ignoredColor) {
    for (var i = 0; i < ignoredColor.length; i++) {
        if (isIgnoredColorAsNumbers(data, index, ignoredColor[i])) {
            return true;
        }
    }
    return false;
}
function isIgnoredColorAsNumbers(data, index, ignoredColor) {
    switch (ignoredColor.length) {
        case 3:
            // [red, green, blue]
            if (isIgnoredRGBColor(data, index, ignoredColor)) {
                return true;
            }
            break;
        case 4:
            // [red, green, blue, alpha]
            if (isIgnoredRGBAColor(data, index, ignoredColor)) {
                return true;
            }
            break;
        case 5:
            // [red, green, blue, alpha, threshold]
            if (isIgnoredRGBAColorWithThreshold(data, index, ignoredColor)) {
                return true;
            }
            break;
        default:
            return false;
    }
}
function isIgnoredRGBColor(data, index, ignoredColor) {
    // Ignore if the pixel are transparent.
    if (data[index + 3] !== 255) {
        return true;
    }
    if (data[index] === ignoredColor[0] &&
        data[index + 1] === ignoredColor[1] &&
        data[index + 2] === ignoredColor[2]) {
        return true;
    }
    return false;
}
function isIgnoredRGBAColor(data, index, ignoredColor) {
    if (data[index + 3] && ignoredColor[3]) {
        return data[index] === ignoredColor[0] &&
            data[index + 1] === ignoredColor[1] &&
            data[index + 2] === ignoredColor[2] &&
            data[index + 3] === ignoredColor[3];
    }
    // Ignore rgb components if the pixel are fully transparent.
    return data[index + 3] === ignoredColor[3];
}
function inRange(colorComponent, ignoredColorComponent, value) {
    return colorComponent >= (ignoredColorComponent - value) &&
        colorComponent <= (ignoredColorComponent + value);
}
function isIgnoredRGBAColorWithThreshold(data, index, ignoredColor) {
    var redIgnored = ignoredColor[0];
    var greenIgnored = ignoredColor[1];
    var blueIgnored = ignoredColor[2];
    var alphaIgnored = ignoredColor[3];
    var threshold = ignoredColor[4];
    var alphaData = data[index + 3];
    var alphaInRange = inRange(alphaData, alphaIgnored, threshold);
    if (!alphaIgnored) {
        return alphaInRange;
    }
    if (!alphaData && alphaInRange) {
        return true;
    }
    if (inRange(data[index], redIgnored, threshold) &&
        inRange(data[index + 1], greenIgnored, threshold) &&
        inRange(data[index + 2], blueIgnored, threshold) &&
        alphaInRange) {
        return true;
    }
    return false;
}
var DEFAULT_DOMINANT_DIVIDER = 24;
function dominantAlgorithm(arr, len, options) {
    var colorHash = {};
    var divider = options.dominantDivider || DEFAULT_DOMINANT_DIVIDER;
    var ignoredColor = options.ignoredColor;
    var step = options.step;
    var max = [0, 0, 0, 0, 0];
    for (var i = 0; i < len; i += step) {
        var red = arr[i];
        var green = arr[i + 1];
        var blue = arr[i + 2];
        var alpha = arr[i + 3];
        if (ignoredColor && isIgnoredColor(arr, i, ignoredColor)) {
            continue;
        }
        var key = Math.round(red / divider) + ',' +
            Math.round(green / divider) + ',' +
            Math.round(blue / divider);
        if (colorHash[key]) {
            colorHash[key] = [
                colorHash[key][0] + red * alpha,
                colorHash[key][1] + green * alpha,
                colorHash[key][2] + blue * alpha,
                colorHash[key][3] + alpha,
                colorHash[key][4] + 1
            ];
        }
        else {
            colorHash[key] = [red * alpha, green * alpha, blue * alpha, alpha, 1];
        }
        if (max[4] < colorHash[key][4]) {
            max = colorHash[key];
        }
    }
    var redTotal = max[0];
    var greenTotal = max[1];
    var blueTotal = max[2];
    var alphaTotal = max[3];
    var count = max[4];
    return alphaTotal ? [
        Math.round(redTotal / alphaTotal),
        Math.round(greenTotal / alphaTotal),
        Math.round(blueTotal / alphaTotal),
        Math.round(alphaTotal / count)
    ] : options.defaultColor;
}
function simpleAlgorithm(arr, len, options) {
    var redTotal = 0;
    var greenTotal = 0;
    var blueTotal = 0;
    var alphaTotal = 0;
    var count = 0;
    var ignoredColor = options.ignoredColor;
    var step = options.step;
    for (var i = 0; i < len; i += step) {
        var alpha = arr[i + 3];
        var red = arr[i] * alpha;
        var green = arr[i + 1] * alpha;
        var blue = arr[i + 2] * alpha;
        if (ignoredColor && isIgnoredColor(arr, i, ignoredColor)) {
            continue;
        }
        redTotal += red;
        greenTotal += green;
        blueTotal += blue;
        alphaTotal += alpha;
        count++;
    }
    return alphaTotal ? [
        Math.round(redTotal / alphaTotal),
        Math.round(greenTotal / alphaTotal),
        Math.round(blueTotal / alphaTotal),
        Math.round(alphaTotal / count)
    ] : options.defaultColor;
}
function sqrtAlgorithm(arr, len, options) {
    var redTotal = 0;
    var greenTotal = 0;
    var blueTotal = 0;
    var alphaTotal = 0;
    var count = 0;
    var ignoredColor = options.ignoredColor;
    var step = options.step;
    for (var i = 0; i < len; i += step) {
        var red = arr[i];
        var green = arr[i + 1];
        var blue = arr[i + 2];
        var alpha = arr[i + 3];
        if (ignoredColor && isIgnoredColor(arr, i, ignoredColor)) {
            continue;
        }
        redTotal += red * red * alpha;
        greenTotal += green * green * alpha;
        blueTotal += blue * blue * alpha;
        alphaTotal += alpha;
        count++;
    }
    return alphaTotal ? [
        Math.round(Math.sqrt(redTotal / alphaTotal)),
        Math.round(Math.sqrt(greenTotal / alphaTotal)),
        Math.round(Math.sqrt(blueTotal / alphaTotal)),
        Math.round(alphaTotal / count)
    ] : options.defaultColor;
}
function getDefaultColor(options) {
    return getOption(options, 'defaultColor', [0, 0, 0, 0]);
}
function getOption(options, name, defaultValue) {
    return (options[name] === undefined ? defaultValue : options[name]);
}
var MIN_SIZE = 10;
var MAX_SIZE = 100;
function isSvg(filename) {
    return filename.search(/\.svg(\?|$)/i) !== -1;
}
function getOriginalSize(resource) {
    if (isInstanceOfHTMLImageElement(resource)) {
        var width = resource.naturalWidth;
        var height = resource.naturalHeight;
        // For SVG images with only viewBox attribute
        if (!resource.naturalWidth && isSvg(resource.src)) {
            width = height = MAX_SIZE;
        }
        return {
            width: width,
            height: height,
        };
    }
    if (isInstanceOfHTMLVideoElement(resource)) {
        return {
            width: resource.videoWidth,
            height: resource.videoHeight
        };
    }
    if (isInstanceOfVideoFrame(resource)) {
        return {
            width: resource.codedWidth,
            height: resource.codedHeight,
        };
    }
    return {
        width: resource.width,
        height: resource.height
    };
}
function getSrc(resource) {
    if (isInstanceOfHTMLCanvasElement(resource)) {
        return 'canvas';
    }
    if (isInstanceOfOffscreenCanvas(resource)) {
        return 'offscreencanvas';
    }
    if (isInstanceOfVideoFrame(resource)) {
        return 'videoframe';
    }
    if (isInstanceOfImageBitmap(resource)) {
        return 'imagebitmap';
    }
    return resource.src;
}
function isInstanceOfHTMLImageElement(resource) {
    return typeof HTMLImageElement !== 'undefined' && resource instanceof HTMLImageElement;
}
var hasOffscreenCanvas = typeof OffscreenCanvas !== 'undefined';
function isInstanceOfOffscreenCanvas(resource) {
    return hasOffscreenCanvas && resource instanceof OffscreenCanvas;
}
function isInstanceOfHTMLVideoElement(resource) {
    return typeof HTMLVideoElement !== 'undefined' && resource instanceof HTMLVideoElement;
}
function isInstanceOfVideoFrame(resource) {
    return typeof VideoFrame !== 'undefined' && resource instanceof VideoFrame;
}
function isInstanceOfHTMLCanvasElement(resource) {
    return typeof HTMLCanvasElement !== 'undefined' && resource instanceof HTMLCanvasElement;
}
function isInstanceOfImageBitmap(resource) {
    return typeof ImageBitmap !== 'undefined' && resource instanceof ImageBitmap;
}
function prepareSizeAndPosition(originalSize, options) {
    var srcLeft = getOption(options, 'left', 0);
    var srcTop = getOption(options, 'top', 0);
    var srcWidth = getOption(options, 'width', originalSize.width);
    var srcHeight = getOption(options, 'height', originalSize.height);
    var destWidth = srcWidth;
    var destHeight = srcHeight;
    if (options.mode === 'precision') {
        return {
            srcLeft: srcLeft,
            srcTop: srcTop,
            srcWidth: srcWidth,
            srcHeight: srcHeight,
            destWidth: destWidth,
            destHeight: destHeight
        };
    }
    var factor;
    if (srcWidth > srcHeight) {
        factor = srcWidth / srcHeight;
        destWidth = MAX_SIZE;
        destHeight = Math.round(destWidth / factor);
    }
    else {
        factor = srcHeight / srcWidth;
        destHeight = MAX_SIZE;
        destWidth = Math.round(destHeight / factor);
    }
    if (destWidth > srcWidth || destHeight > srcHeight ||
        destWidth < MIN_SIZE || destHeight < MIN_SIZE) {
        destWidth = srcWidth;
        destHeight = srcHeight;
    }
    return {
        srcLeft: srcLeft,
        srcTop: srcTop,
        srcWidth: srcWidth,
        srcHeight: srcHeight,
        destWidth: destWidth,
        destHeight: destHeight
    };
}
var isWebWorkers = typeof window === 'undefined';
function makeCanvas() {
    if (isWebWorkers) {
        return hasOffscreenCanvas ? new OffscreenCanvas(1, 1) : null;
    }
    return document.createElement('canvas');
}
var ERROR_PREFIX = 'FastAverageColor: ';
function getError(message) {
    return Error(ERROR_PREFIX + message);
}
function outputError(error, silent) {
    if (!silent) {
        console.error(error);
    }
}
var FastAverageColor = /** @class */ (function () {
    function FastAverageColor() {
        this.canvas = null;
        this.ctx = null;
    }
    FastAverageColor.prototype.getColorAsync = function (resource, options) {
        if (!resource) {
            return Promise.reject(getError('call .getColorAsync() without resource'));
        }
        if (typeof resource === 'string') {
            // Web workers
            if (typeof Image === 'undefined') {
                return Promise.reject(getError('resource as string is not supported in this environment'));
            }
            var img = new Image();
            img.crossOrigin = options && options.crossOrigin || '';
            img.src = resource;
            return this.bindImageEvents(img, options);
        }
        else if (isInstanceOfHTMLImageElement(resource) && !resource.complete) {
            return this.bindImageEvents(resource, options);
        }
        else {
            var result = this.getColor(resource, options);
            return result.error ? Promise.reject(result.error) : Promise.resolve(result);
        }
    };
    /**
     * Get the average color from images, videos and canvas.
     */
    FastAverageColor.prototype.getColor = function (resource, options) {
        options = options || {};
        var defaultColor = getDefaultColor(options);
        if (!resource) {
            var error = getError('call .getColor() without resource');
            outputError(error, options.silent);
            return this.prepareResult(defaultColor, error);
        }
        var originalSize = getOriginalSize(resource);
        var size = prepareSizeAndPosition(originalSize, options);
        if (!size.srcWidth || !size.srcHeight || !size.destWidth || !size.destHeight) {
            var error = getError("incorrect sizes for resource \"".concat(getSrc(resource), "\""));
            outputError(error, options.silent);
            return this.prepareResult(defaultColor, error);
        }
        if (!this.canvas) {
            this.canvas = makeCanvas();
            if (!this.canvas) {
                var error = getError('OffscreenCanvas is not supported in this browser');
                outputError(error, options.silent);
                return this.prepareResult(defaultColor, error);
            }
        }
        if (!this.ctx) {
            this.ctx = this.canvas.getContext('2d', { willReadFrequently: true });
            if (!this.ctx) {
                var error = getError('Canvas Context 2D is not supported in this browser');
                outputError(error, options.silent);
                return this.prepareResult(defaultColor);
            }
            this.ctx.imageSmoothingEnabled = false;
        }
        this.canvas.width = size.destWidth;
        this.canvas.height = size.destHeight;
        try {
            this.ctx.clearRect(0, 0, size.destWidth, size.destHeight);
            this.ctx.drawImage(resource, size.srcLeft, size.srcTop, size.srcWidth, size.srcHeight, 0, 0, size.destWidth, size.destHeight);
            var bitmapData = this.ctx.getImageData(0, 0, size.destWidth, size.destHeight).data;
            return this.prepareResult(this.getColorFromArray4(bitmapData, options));
        }
        catch (originalError) {
            var error = getError("security error (CORS) for resource ".concat(getSrc(resource), ".\nDetails: https://developer.mozilla.org/en/docs/Web/HTML/CORS_enabled_image"));
            outputError(error, options.silent);
            if (!options.silent) {
                console.error(originalError);
            }
            return this.prepareResult(defaultColor, error);
        }
    };
    /**
     * Get the average color from a array when 1 pixel is 4 bytes.
     */
    FastAverageColor.prototype.getColorFromArray4 = function (arr, options) {
        options = options || {};
        var bytesPerPixel = 4;
        var arrLength = arr.length;
        var defaultColor = getDefaultColor(options);
        if (arrLength < bytesPerPixel) {
            return defaultColor;
        }
        var len = arrLength - arrLength % bytesPerPixel;
        var step = (options.step || 1) * bytesPerPixel;
        var algorithm;
        switch (options.algorithm || 'sqrt') {
            case 'simple':
                algorithm = simpleAlgorithm;
                break;
            case 'sqrt':
                algorithm = sqrtAlgorithm;
                break;
            case 'dominant':
                algorithm = dominantAlgorithm;
                break;
            default:
                throw getError("".concat(options.algorithm, " is unknown algorithm"));
        }
        return algorithm(arr, len, {
            defaultColor: defaultColor,
            ignoredColor: prepareIgnoredColor(options.ignoredColor),
            step: step,
            dominantDivider: options.dominantDivider,
        });
    };
    /**
     * Get color data from value ([r, g, b, a]).
     */
    FastAverageColor.prototype.prepareResult = function (value, error) {
        var rgb = value.slice(0, 3);
        var rgba = [value[0], value[1], value[2], value[3] / 255];
        var isDarkColor = isDark(value);
        return {
            value: [value[0], value[1], value[2], value[3]],
            rgb: 'rgb(' + rgb.join(',') + ')',
            rgba: 'rgba(' + rgba.join(',') + ')',
            hex: arrayToHex(rgb),
            hexa: arrayToHex(value),
            isDark: isDarkColor,
            isLight: !isDarkColor,
            error: error,
        };
    };
    /**
     * Destroy the instance.
     */
    FastAverageColor.prototype.destroy = function () {
        if (this.canvas) {
            this.canvas.width = 1;
            this.canvas.height = 1;
            this.canvas = null;
        }
        this.ctx = null;
    };
    FastAverageColor.prototype.bindImageEvents = function (resource, options) {
        var _this = this;
        return new Promise(function (resolve, reject) {
            var onload = function () {
                unbindEvents();
                var result = _this.getColor(resource, options);
                if (result.error) {
                    reject(result.error);
                }
                else {
                    resolve(result);
                }
            };
            var onerror = function () {
                unbindEvents();
                reject(getError("Error loading image \"".concat(resource.src, "\"")));
            };
            var onabort = function () {
                unbindEvents();
                reject(getError("Image \"".concat(resource.src, "\" loading aborted")));
            };
            var unbindEvents = function () {
                resource.removeEventListener('load', onload);
                resource.removeEventListener('error', onerror);
                resource.removeEventListener('abort', onabort);
            };
            resource.addEventListener('load', onload);
            resource.addEventListener('error', onerror);
            resource.addEventListener('abort', onabort);
        });
    };
    return FastAverageColor;
}());
if (typeof window !== 'undefined') {
    window.FastAverageColor = FastAverageColor;
}

// ==================================================
// 你的 CoverThemeColor 类 (已修改)
// ==================================================

class CoverThemeColor {
    constructor() {
        this.fac = null;
        this.currentProtyle = null;
        this.backgroundObserver = null; 
        this.tabObserver = null;  
        this.lastProcessedUrl = '';
        this.debounceTimer = null;

        // 直接实例化内嵌的 FastAverageColor
        if (window.FastAverageColor) {
            this.fac = new window.FastAverageColor();
            console.log('CoverThemeColor: (诊断) FastAverageColor 已内嵌并实例化。');
        } else {
            console.warn('CoverThemeColor: (诊断) 内嵌 FastAverageColor 失败。图片题头图功能将不可用。');
        }

        this.init();
    }

    init() {
        // 不再需要 loadScript
        this.findTabBar();
    }

    findTabBar() {
        const tabBar = document.querySelector('.layout__center .layout-tab-bar');

        if (tabBar) {
            console.log('CoverThemeColor (诊断): 成功找到 Tab 栏, 启动监视器。');
            this.setupTabObserver(tabBar);
    
            this.handleTabSwitch();  
        } else {
            setTimeout(() => this.findTabBar(), 200);
        }
    }

    setupTabObserver(tabBar) {
        this.tabObserver = new MutationObserver(() => {
            clearTimeout(this.debounceTimer);
            this.debounceTimer = setTimeout(() => this.handleTabSwitch(), 150);
        });

        this.tabObserver.observe(tabBar, {
            attributes: true,
            attributeFilter: ['class'], 
            subtree: true 
        });
    }

    handleTabSwitch() {
        const focusedTab = document.querySelector('.layout__center .layout-tab-bar .item--focus');
        if (!focusedTab) {
            console.log('CoverThemeColor (诊断): handleTabSwitch - 未找到 focused tab (.item--focus)');
            return;
        }

        const protyleId = focusedTab.dataset.id;
        if (!protyleId) {
            console.log('CoverThemeColor (诊断): handleTabSwitch - Focused tab 没有 data-id');
            return;
        }

        const protyleElement = document.querySelector(`.layout__center .protyle[data-id="${protyleId}"]`);
        if (!protyleElement) {
            console.log(`CoverThemeColor (诊断): handleTabSwitch - 未找到 data-id 为 ${protyleId} 的 protyle`);
            return;
        }

        if (protyleElement === this.currentProtyle) {
            return;
        }

        console.log(`CoverThemeColor (诊断): handleTabSwitch - 检测到文档切换! 新 ID: ${protyleId}`);
        this.currentProtyle = protyleElement;

        this.setupBackgroundObserver(protyleElement);
    }

    setupBackgroundObserver(protyleElement) {
        if (this.backgroundObserver) {
            this.backgroundObserver.disconnect();
            this.backgroundObserver = null;
        }

        const backgroundEl = protyleElement.querySelector('.protyle-background');

        if (backgroundEl) {
            this.updateColor();

            this.backgroundObserver = new MutationObserver(() => {
                clearTimeout(this.debounceTimer);
                this.debounceTimer = setTimeout(() => this.updateColor(), 150);
            });

            this.backgroundObserver.observe(backgroundEl, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['src', 'style']
            });
        } else {
            this.resetColor();
        }
    }

    // loadScript 方法已被移除

    updateColor() {
        if (!this.currentProtyle) {
             console.log('CoverThemeColor (诊断): updateColor - 失败, this.currentProtyle 为空。');
            return;
        }

        const img = this.currentProtyle.querySelector('.protyle-background__img img');

        if (!img) {
            this.resetColor();
            return;
        }

        const style = img.getAttribute('style');
        if (style && style.includes('linear-gradient')) {
    
            if (style === this.lastProcessedUrl) return; 
    
            console.log('CoverThemeColor (诊断): updateColor - 找到渐变色 (linear-gradient)。');
    
            const gradientRegex = /linear-gradient\([^,]+,([^,)]+)/;
            const colorMatch = style.match(gradientRegex);
    
            if (colorMatch && colorMatch[1]) {
                const colorRegex = /(#[0-9a-fA-F]{3,6}|rgba?\([^)]+\))/;
                const finalColorMatch = colorMatch[1].match(colorRegex);
        
                if (finalColorMatch && finalColorMatch[1]) {
                    const color = finalColorMatch[1].trim();
                    console.log(`CoverThemeColor (诊断): 提取到渐变色: ${color}`);
                    this.applyColor(color); 
                    this.lastProcessedUrl = style; 
                    return; 
                }
            }
            console.log('CoverThemeColor (诊断): 找到了渐变色, 但提取颜色失败。');
        }

        if (!this.fac) {
            if (img.src && img.src.length > 200) {
                 console.log('CoverThemeColor (诊断): updateColor - 找到图片, 但 fac 未加载, 无法处理。');
            }
            this.resetColor();
            return;
        }

        if (img.src) {
    
            if (img.src === this.lastProcessedUrl) {
                return; 
            }
    
            if (img.complete) {
                this.processImage(img);
            } else {
                img.onload = () => this.processImage(img);
                img.onerror = () => {
                    console.error('CoverThemeColor: 图片加载失败。', img.src);
                    this.lastProcessedUrl = img.src;  
                };
            }
        } else {
            this.resetColor();
        }
    }

    processImage(img) {
        if (!img || !img.src || (img.src.startsWith('data:image/') && img.src.length < 200)) {
            if (this.lastProcessedUrl !== img.src) {
                this.resetColor();
            }
            this.lastProcessedUrl = img.src;
            return;
        }

        this.lastProcessedUrl = img.src;
        console.log(`CoverThemeColor (诊断): processImage - 正在处理图片: ${img.src.substring(0, 50)}...`);

        this.fac.getColorAsync(img)  
            .then(color => {
                if (color) {
                    this.applyColor(color.hex); 
                }
            })
            .catch(e => {
                console.error('CoverThemeColor: 提取颜色失败 (fac.getColorAsync 失败)。', e);
                this.resetColor();
            });
    }

    applyColor(color) {
        console.log(`CoverThemeColor (诊断): applyColor - 正在应用颜色: ${color}`);
        const rootStyle = document.documentElement.style;
        rootStyle.setProperty('--b3-theme-primary', color);

        let hexColor = color;
        if (color.startsWith('rgb')) {
            hexColor = this.rgbToHex(color);
        }

        if (!hexColor || !hexColor.startsWith('#') || hexColor.length < 4) {
             console.warn('CoverThemeColor: 无法计算变体, 颜色不是有效的Hex或RGB:', color);
             rootStyle.removeProperty('--b3-theme-primary-light');
             rootStyle.removeProperty('--b3-theme-primary-dark');
            return;
        }

        try {
            const lightColor = this.adjustColor(hexColor, 20); 
            const darkColor = this.adjustColor(hexColor, -20); 
            rootStyle.setProperty('--b3-theme-primary-light', lightColor);
            rootStyle.setProperty('--b3-theme-primary-dark', darkColor);
        } catch (e) {
            console.error('CoverThemeColor: 计算亮/暗变体失败。', e);
        }
    }

    resetColor() {
        if (this.lastProcessedUrl !== '') { 
            console.log('CoverThemeColor (诊断): resetColor - 正在重置颜色。');
        }
        this.lastProcessedUrl = ''; 
        const rootStyle = document.documentElement.style;
        rootStyle.removeProperty('--b3-theme-primary');
        rootStyle.removeProperty('--b3-theme-primary-light');
        rootStyle.removeProperty('--b3-theme-primary-dark');
    }

    adjustColor(color, percent) {
        if (color.length === 4) {
            color = `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}`;
        }
  
        let r = parseInt(color.substring(1, 3), 16);
        let g = parseInt(color.substring(3, 5), 16);
        let b = parseInt(color.substring(5, 7), 16);

        const amount = Math.round(2.55 * percent);

        r = Math.max(0, Math.min(255, r + amount));
        g = Math.max(0, Math.min(255, g + amount));
        b = Math.max(0, Math.min(255, b + amount));

        const rr = r.toString(16).padStart(2, '0');
        const gg = g.toString(16).padStart(2, '0');
        const bb = b.toString(16).padStart(2, '0');

        return `#${rr}${gg}${bb}`;
    }
  
    rgbToHex(rgb) {
        try {
            const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
            if (!match) return '#000000'; 
            const componentToHex = (c) => {
                const hex = parseInt(c, 10).toString(16);
                return hex.length == 1 ? "0" + hex : hex;
            };
            return "#" + componentToHex(match[1]) + componentToHex(match[2]) + componentToHex(match[3]);
        } catch (e) {
            console.error('CoverThemeColor: rgbToHex 转换失败', e);
            return '#000000';
        }
    }
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => {
        window.coverThemeColorSnippet = new CoverThemeColor();
    });
} else {
    if (document.body) {
         window.coverThemeColorSnippet = new CoverThemeColor();
    } else {
         document.addEventListener('DOMContentLoaded', () => {
            window.coverThemeColorSnippet = new CoverThemeColor();
        });
    }
}

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    28445 引用 • 119766 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    285 引用 • 1985 回帖
1 操作
lovexmm521 在 2025-10-25 18:00:29 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • lovexmm521

    我不知道你需要那个容器换,也不能给别人瞎换容器 我只能给你个 css 容器作为参考 然后 ai 直接代码添加就行

    [data-theme-mode="light"] {
        --QYL-custom-primary-main: 292deg;
        /* 主色 */
        --b3-theme-primary: #8A5CF5;
        --b3-theme-primary-light: rgba(138, 92, 245, 0.55);
        --b3-theme-primary-lighter: rgba(138, 92, 245, 0.32);
        --b3-theme-primary-lightest: rgba(138, 92, 245, 0.15);
        /* 编辑器背景色 */
        --b3-theme-background: #f6f5fa;
        --b3-theme-background-light: #e3e1ec;
        /* 面板背景色 */
        --b3-theme-surface: #eeedf5;
        --b3-theme-surface-light: rgba(239, 237, 245, 0.8);
        --b3-theme-surface-lighter: rgba(181, 176, 211, 0.68);
        /* 文字颜色 */
        --b3-theme-on-primary: rgba(255,255,255,.9);
        --b3-theme-on-error: var(--b3-theme-on-primary);
        --b3-theme-on-secondary: var(--b3-theme-on-primary);
        --b3-theme-on-background: #11253e;
        --b3-theme-on-surface: #617691;
        --b3-theme-on-surface-light: #617691;
        /* 顶部工具栏 */
        --b3-toolbar-blur-background: #f3f6f8;
        --b3-toolbar-color: var(--b3-theme-on-surface-light);
        --b3-toolbar-hover: var(--b3-list-hover);
        /* 滚动条 */
        --b3-scroll-color: #dbd5eb;
        /* 悬停 */
        --b3-list-hover: rgba(210, 194, 249, 0.8);
        --b3-list-icon-hover: rgba(210, 194, 249, 0.8);
        /* 菜单 */
        --b3-menu-background: var(--b3-theme-background);
        /* 提示 */
        --b3-tooltips-background: var(--b3-theme-background);
        --b3-tooltips-color: var(--b3-theme-on-surface);
        --b3-tooltips-second-color: var(--b3-theme-on-surface-light);
        --b3-tooltips-shadow: var(--b3-point-shadow);
        /* 遮罩 */
        --b3-mask-background: rgba(220, 220, 220, 0.4);
        /* switch */
        --b3-switch-background: #d4dadf;
        /* 行级代码背景色 */
        --b3-protyle-code-background: var(--b3-theme-surface);
        /* 页签 */
        --QYL-tab-item: rgba(212, 221, 233, 0.3);
        --QYL-tab-item-focus: rgba(210, 194, 249, 0.8);
        /* 状态栏 */
        --QYL-status-background: var(--b3-theme-background);
        /* 毛玻璃 */
        --QYL-Aero-background: rgba(246, 245, 250, 0.7);
        --QYL-Aero-background-wrap: rgba(246, 245, 250, 0.7);
        /* 引述块 */
        --QYL-blockquote-background: #ebeaf2;
        --QYL-blockquote-svg: #c9c9d4;
        --QYL-blockquote: #7d7d86;
        /* 行级代码 */
        --QYL-inline-code: var(--b3-theme-primary);
    }
    /* 细节调整 */
    .QYLColorBlock[data-theme-mode="light"] {
        --b3-theme-surface: #e6e3f4;
    }
    
  • 其他回帖
  • stevehfut

    FastAverageColor.js 有办法一起集成到代码片段吗(要单独插个文件话比较麻烦)

  • lovexmm521

    ok,上一个版是尊重 QYL 大佬版,现在是完整版

  • stevehfut

    目前有按钮等变化,其他变化自行添加 css 容器

    具体如何操作

    2 回复
  • 查看全部回帖
lovexmm521
窈窕淑女,君子好逑 爱发电:https://afdian.com/a/QianQian517

推荐标签 标签

  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    502 引用 • 1397 回帖 • 240 关注
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    23 引用 • 22 回帖
  • 导航

    各种网址链接、内容导航。

    45 引用 • 177 回帖
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 44 关注
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 563 关注
  • 工具

    子曰:“工欲善其事,必先利其器。”

    308 引用 • 773 回帖
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 429 关注
  • 深度学习

    深度学习(Deep Learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。

    45 引用 • 44 回帖 • 1 关注
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    134 引用 • 1128 回帖 • 93 关注
  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    8 引用 • 69 回帖 • 6 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖 • 6 关注
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    960 引用 • 946 回帖
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 67 回帖 • 436 关注
  • 京东

    京东是中国最大的自营式电商企业,2015 年第一季度在中国自营式 B2C 电商市场的占有率为 56.3%。2014 年 5 月,京东在美国纳斯达克证券交易所正式挂牌上市(股票代码:JD),是中国第一个成功赴美上市的大型综合型电商平台,与腾讯、百度等中国互联网巨头共同跻身全球前十大互联网公司排行榜。

    14 引用 • 102 回帖 • 260 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 693 关注
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 203 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 3 关注
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 531 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 475 关注
  • 阿里云

    阿里云是阿里巴巴集团旗下公司,是全球领先的云计算及人工智能科技公司。提供云服务器、云数据库、云安全等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。

    85 引用 • 324 回帖
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 60 关注
  • ActiveMQ

    ActiveMQ 是 Apache 旗下的一款开源消息总线系统,它完整实现了 JMS 规范,是一个企业级的消息中间件。

    19 引用 • 13 回帖 • 707 关注
  • Thymeleaf

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 Velocity、 FreeMarker 等,它也可以轻易的与 Spring 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。

    11 引用 • 19 回帖 • 413 关注
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖 • 2 关注
  • V2Ray
    1 引用 • 15 回帖 • 4 关注