Show Console Messages

Displays console messages (log, error, warn, info, debug) on screen with styled notifications.

当前为 2024-10-09 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Show Console Messages
// @namespace   Kyan Violentmonkey Scripts
// @match       *://*/*
// @exclude     *://*.bilibili.com/*
// @grant       none
// @license     MIT
// @version     1.2.5
// @author      Kyan
// @description Displays console messages (log, error, warn, info, debug) on screen with styled notifications.
// ==/UserScript==
(function () {
    'use strict'

    // CSS styles
    let console_msg_styles = document.createElement('style')
    console_msg_styles.innerHTML = `
        .console-message-div {
            position: fixed;
            z-index: 999;
            bottom: 2em;
            right: 2em;
            background: transparent;
            display: flex;
            flex-wrap: wrap;
            flex-direction: column-reverse;
            transition: all 1s ease-out;  /* for slide_show() */
            overflow: hidden;  /* for slide_show() */
            pointer-events: none;
        }
        .console-message-wrapper {
            width: 100%;
            background: transparent;
            transition: all 1s ease-out;
            position: relative;
        }
        .console-message-wrapper-progress-bar {
            position: absolute;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: transparent;
            border-radius: 5px;
            z-index: -1;
            overflow: hidden;  /* hide border-radius of .console-message-wrapper-progress */
        }
        .console-message-wrapper-progress {
            height: 100%;
            background-color: rgba(94, 129, 172, 0.5);  /* nord10 Frost (Darkest) */
            width: 100%;
            transition: all 1s linear;
            transform-origin: right;
        }
        .console-message-text {
            float: right;
            padding: 5px;
            margin: 5px;
            border-radius: 5px;
            width: fit-content;
            font-size: small;
            transition: all 1s ease-out;
            font-family: monospace, sans-serif;
            white-space: pre-wrap;  /* Preserve whitespace but allow wrapping */
            /* white-space: nowrap;  /* Prevent line breaks */
            word-break: break-word;  /* Break long words */
            overflow: hidden;     /* Hide overflowed content */
            text-overflow: ellipsis;  /* Show ellipsis for overflowed content */
            max-width: 62vw;
            pointer-events: auto;
            text-shadow: 0px 0px 5px #2e3440;  /* nord0 Polar Night (Darkest) */
        }
        .cursor-pointer {
            cursor: pointer;
        }
        .bg-blur {
            backdrop-filter: blur(5px);
        }
    `;
    document.head.appendChild(console_msg_styles);

    // create a div to show console log
    let console_msg_div = document.createElement('div');
    console_msg_div.classList.add('console-message-div');
    document.body.appendChild(console_msg_div);


    // override the default console.log function
    let original_console_log = console.log;
    let original_console_error = console.error;
    let original_console_warn = console.warn;
    let original_console_info = console.info;
    let original_console_debug = console.debug;

    const exit_to_right = (ele) => {
        if (typeof ele !== 'undefined') {
            ele.style.opacity = 0;
            ele.style.transform = 'translateX(200%)';
            setTimeout(() => ele.remove(), 1000);
        }
    }
    const slide_show = (ele) => {
        if (typeof ele !== 'undefined') {
          let ele_height = window.getComputedStyle(ele).height  // Get real height (including padding)
          ele.style.height = '0'  // Init height with 0px
          // ele.offsetHeight  // Trigger a browser repaint and force reflow to ensure the height of 0px is calculated
          requestAnimationFrame(() => {  // Executed on next repaint cycle
              ele.style.height = ele_height  // Recover element height
          })
        }
    }

    const console_call = (type, original_function, ...args) => {
        original_function(...args)
        let skip_next = false  // If next arg is %c styles
        let message = args.map(arg => {
            if (skip_next) {
                skip_next = false
                return
            }
            if (typeof arg === 'string' && arg.includes('%c')) {  // If arg has %c in it
                skip_next = true  // Next arg will be the %c styles
                return arg.replace(/%c/g, '')  // Remove %c from message
            }
            if (typeof arg === 'object' && arg !== null) {
                if (arg instanceof Error) {
                    return arg.message
                }
                return JSON.stringify(arg, null, 4)
            }
            return String(arg)
        }).join('\n')
        if (message === 'bl') {
            return
        }
        // Check if the last message is the same as the current one
        let last_msglet_wrapper = console_msg_div.lastChild  // document.querySelector('.console-message-wrapper')
        if (last_msglet_wrapper) {
            let last_msglet_message = last_msglet_wrapper.querySelector('.msglet-message').textContent
            let last_msglet_count = last_msglet_wrapper.querySelector('.msglet-count')

            if (last_msglet_message === message) {
                // If the messages are the same, update the count
                let count = parseInt(last_msglet_wrapper.getAttribute('data-dup-count'))
                count += 1
                last_msglet_wrapper.setAttribute('data-dup-count', count)
                last_msglet_count.textContent = `×${count}`
                return  // Exit, no need to create a new message
            }
        }

        // Create new msglet
        let msglet_wrapper = document.createElement('div')
        msglet_wrapper.classList.add('console-message-wrapper')
        msglet_wrapper.setAttribute('data-dup-count', 1)
        let msglet = document.createElement('div')
        msglet.classList.add('console-message-text', 'bg-blur', 'cursor-pointer')
        msglet.addEventListener('click', () => exit_to_right(msglet_wrapper))

        // add flair and style
        let flair = ''
        let transparency = 0.618
        switch (type) {
            case 'log':
                flair = '💡'
                msglet.style.color = 'white'
                msglet.style.backgroundColor = `rgba(46, 52, 64, ${transparency})`  // nord0 Polar Night (Darkest)
                break
            case 'error':
                flair = '❌'
                msglet.style.color = 'white'
                msglet.style.backgroundColor = `rgba(191, 97, 106, ${transparency})`  // nord11 Aurora (Red)
                break
            case 'warn':
                flair = '⚠️'
                msglet.style.color = '#EBCB8B'  // nord13 Aurora (Yellow)
                msglet.style.backgroundColor = `rgba(46, 52, 64, ${transparency})`
                break
            case 'info':
                flair = 'ℹ️'
                msglet.style.color = 'white'
                msglet.style.backgroundColor = `rgba(163, 190, 140, ${transparency})`  // nord14 Aurora (Green)
                break
            case 'debug':
                flair = '🐛'
                msglet.style.color = 'white'
                msglet.style.backgroundColor = `rgba(180, 142, 173, ${transparency})`  // nord15 Aurora (Purple)
                break
            default:
                flair = `[${type}]`
                msglet.style.color = '#ECEFF4'  // nord6 Snow Storm (Lightest)
                msglet.style.backgroundColor = `rgba(46, 52, 64, ${transparency})`  // nord0 Polar Night (Darkest)
                break
        }
        msglet.innerHTML = `<span>${flair}</span> <span class='msglet-message'>${message}</span> <span class='msglet-count'><span>`
        msglet_wrapper.appendChild(msglet)
        console_msg_div.prepend(msglet_wrapper)
        slide_show(msglet_wrapper)
        // Calculate the time left of the message
        let lifespan = Math.min(1000 + message.length * 100, 5000)
        // Generate a progress bar
        let progress_bar = document.createElement('div')
        progress_bar.classList.add('console-message-wrapper-progress-bar')
        let progress = document.createElement('div')
        progress.classList.add('console-message-wrapper-progress')
        progress_bar.appendChild(progress)
        msglet.appendChild(progress_bar)
        progress.style.transitionDuration = lifespan + 'ms'
        // Animate the progress bar
        setTimeout(() => {
            progress.style.transform = 'scaleX(0)'
        }, 100)
        // Easeout the message after few seconds
        progress.addEventListener('transitionend', () => {
            if (progress.style.transform === 'scaleX(0)') {
                exit_to_right(msglet_wrapper)
            }
        })
    }

    // Monkey Patch
    if (window.location.hostname !== 'localhost') {  // Only works on host other than localhost
      console.log = (...args) => console_call('log', original_console_log, ...args)
      console.error = (...args) => console_call('error', original_console_error, ...args)
      console.warn = (...args) => console_call('warn', original_console_warn, ...args)
      console.info = (...args) => console_call('info', original_console_info, ...args)
      console.debug = (...args) => console_call('debug', original_console_debug, ...args)
    }
})();