显示控制台消息

在屏幕上显示控制台消息(`console.*`),并提供样式化的通知。

  1. // ==UserScript==
  2. // @name Show Console Messages
  3. // @name:zh-CN 显示控制台消息
  4. // @name:zh-TW 顯示控制台消息
  5. // @name:fr Afficher les messages de la console
  6. // @name:es Mostrar mensajes de la consola
  7. // @name:ru Отображение сообщений консоли
  8. // @name:ja コンソールメッセージを表示
  9. // @name:ko 콘솔 메시지 표시
  10. // @description Displays console messages (`console.*`) on screen with styled notifications.
  11. // @description:zh-CN 在屏幕上显示控制台消息(`console.*`),并提供样式化的通知。
  12. // @description:zh-TW 在螢幕上顯示控制台消息(`console.*`),並提供樣式化的通知。
  13. // @description:fr Affiche les messages de la console (`console.*`) à l'écran avec des notifications stylisées.
  14. // @description:es Muestra los mensajes de la consola (`console.*`) en pantalla con notificaciones con estilo.
  15. // @description:ru Отображает сообщения консоли (`console.*`) на экране со стилизованными уведомлениями.
  16. // @description:ja コンソールメッセージ(`console.*`)を画面に表示し、スタイリッシュな通知を提供します。
  17. // @description:ko 콘솔 메시지 (`console.*`)를 화면에 스타일된 알림으로 표시합니다。
  18. // @namespace Kyan Violentmonkey Scripts
  19. // @match *://*.ccugame.app/*
  20. // @grant none
  21. // @license MIT
  22. // @version 2.0.0
  23. // @author Kyan
  24. // ==/UserScript==
  25. (function () {
  26. 'use strict'
  27.  
  28. // CSS styles
  29. let console_msg_styles = document.createElement('style')
  30. console_msg_styles.innerHTML = `
  31. .console-message-div {
  32. position: fixed;
  33. z-index: 999;
  34. bottom: 2em;
  35. right: 2em;
  36. background: transparent;
  37. display: flex;
  38. flex-wrap: wrap;
  39. flex-direction: column-reverse;
  40. transition: all 1s ease-out; /* for slide_show() */
  41. overflow: hidden; /* for slide_show() */
  42. pointer-events: none;
  43. }
  44. .console-message-wrapper {
  45. width: 100%;
  46. background: transparent;
  47. transition: all 1s ease-out;
  48. position: relative;
  49. }
  50. .console-message-wrapper-progress-bar {
  51. position: absolute;
  52. bottom: 0;
  53. left: 0;
  54. width: 100%;
  55. height: 100%;
  56. background-color: transparent;
  57. border-radius: 5px;
  58. z-index: -1;
  59. overflow: hidden; /* hide border-radius of .console-message-wrapper-progress */
  60. }
  61. .console-message-wrapper-progress {
  62. height: 100%;
  63. background-color: rgba(94, 129, 172, 0.5); /* nord10 Frost (Darkest) */
  64. width: 100%;
  65. transition: all 1s linear;
  66. transform-origin: right;
  67. }
  68. .console-message-text {
  69. float: right;
  70. padding: 5px;
  71. margin: 5px;
  72. border-radius: 5px;
  73. width: fit-content;
  74. font-size: small;
  75. transition: all 1s ease-out;
  76. font-family: monospace, sans-serif;
  77. white-space: pre-wrap; /* Preserve whitespace but allow wrapping */
  78. /* white-space: nowrap; /* Prevent line breaks */
  79. word-break: break-word; /* Break long words */
  80. overflow: hidden; /* Hide overflowed content */
  81. text-overflow: ellipsis; /* Show ellipsis for overflowed content */
  82. max-width: 62vw;
  83. pointer-events: auto;
  84. text-shadow: 0px 0px 5px #2e3440; /* nord0 Polar Night (Darkest) */
  85. }
  86. .cursor-pointer {
  87. cursor: pointer;
  88. }
  89. .bg-blur {
  90. backdrop-filter: blur(5px);
  91. }
  92. `;
  93. document.head.appendChild(console_msg_styles);
  94.  
  95. // create a div to show console log
  96. let console_msg_div = document.createElement('div');
  97. console_msg_div.classList.add('console-message-div');
  98. document.body.appendChild(console_msg_div);
  99.  
  100.  
  101. // override the default console.log function
  102. let original_console_log = console.log;
  103. let original_console_error = console.error;
  104. let original_console_warn = console.warn;
  105. let original_console_info = console.info;
  106. let original_console_debug = console.debug;
  107.  
  108. const exit_to_right = (ele) => {
  109. if (typeof ele !== 'undefined') {
  110. ele.style.opacity = 0;
  111. ele.style.transform = 'translateX(200%)';
  112. setTimeout(() => ele.remove(), 1000);
  113. }
  114. }
  115. const slide_show = (ele) => {
  116. if (typeof ele !== 'undefined') {
  117. let ele_height = window.getComputedStyle(ele).height // Get real height (including padding)
  118. ele.style.height = '0' // Init height with 0px
  119. // ele.offsetHeight // Trigger a browser repaint and force reflow to ensure the height of 0px is calculated
  120. requestAnimationFrame(() => { // Executed on next repaint cycle
  121. ele.style.height = ele_height // Recover element height
  122. })
  123. }
  124. }
  125.  
  126. const console_call = (type, original_function, ...args) => {
  127. original_function(...args)
  128. let skip_next = false // If next arg is %c styles
  129. let message = args.map(arg => {
  130. if (skip_next) {
  131. skip_next = false
  132. return
  133. }
  134. if (typeof arg === 'string' && arg.includes('%c')) { // If arg has %c in it
  135. skip_next = true // Next arg will be the %c styles
  136. return arg.replace(/%c/g, '') // Remove %c from message
  137. }
  138. if (typeof arg === 'object' && arg !== null) {
  139. if (arg instanceof Error) {
  140. return arg.message
  141. }
  142. return JSON.stringify(arg, null, 4)
  143. }
  144. return String(arg)
  145. }).join('\n')
  146. if (message === 'bl') {
  147. return
  148. }
  149. // Check if the last message is the same as the current one
  150. let last_msglet_wrapper = console_msg_div.lastChild // document.querySelector('.console-message-wrapper')
  151. if (last_msglet_wrapper) {
  152. let last_msglet_message = last_msglet_wrapper.querySelector('.msglet-message').textContent
  153. let last_msglet_count = last_msglet_wrapper.querySelector('.msglet-count')
  154.  
  155. if (last_msglet_message === message) {
  156. // If the messages are the same, update the count
  157. let count = parseInt(last_msglet_wrapper.getAttribute('data-dup-count'))
  158. count += 1
  159. last_msglet_wrapper.setAttribute('data-dup-count', count)
  160. last_msglet_count.textContent = ${count}`
  161. return // Exit, no need to create a new message
  162. }
  163. }
  164.  
  165. // Create new msglet
  166. let msglet_wrapper = document.createElement('div')
  167. msglet_wrapper.classList.add('console-message-wrapper')
  168. msglet_wrapper.setAttribute('data-dup-count', 1)
  169. let msglet = document.createElement('div')
  170. msglet.classList.add('console-message-text', 'bg-blur', 'cursor-pointer')
  171. msglet.addEventListener('click', () => exit_to_right(msglet_wrapper))
  172.  
  173. // add flair and style
  174. let flair = ''
  175. let transparency = 0.618
  176. switch (type) {
  177. case 'log':
  178. flair = '💡'
  179. msglet.style.color = 'white'
  180. msglet.style.backgroundColor = `rgba(46, 52, 64, ${transparency})` // nord0 Polar Night (Darkest)
  181. break
  182. case 'error':
  183. flair = '❌'
  184. msglet.style.color = 'white'
  185. msglet.style.backgroundColor = `rgba(191, 97, 106, ${transparency})` // nord11 Aurora (Red)
  186. break
  187. case 'warn':
  188. flair = '⚠️'
  189. msglet.style.color = '#EBCB8B' // nord13 Aurora (Yellow)
  190. msglet.style.backgroundColor = `rgba(46, 52, 64, ${transparency})`
  191. break
  192. case 'info':
  193. flair = 'ℹ️'
  194. msglet.style.color = 'white'
  195. msglet.style.backgroundColor = `rgba(163, 190, 140, ${transparency})` // nord14 Aurora (Green)
  196. break
  197. case 'debug':
  198. flair = '🐛'
  199. msglet.style.color = 'white'
  200. msglet.style.backgroundColor = `rgba(180, 142, 173, ${transparency})` // nord15 Aurora (Purple)
  201. break
  202. default:
  203. flair = `[${type}]`
  204. msglet.style.color = '#ECEFF4' // nord6 Snow Storm (Lightest)
  205. msglet.style.backgroundColor = `rgba(46, 52, 64, ${transparency})` // nord0 Polar Night (Darkest)
  206. break
  207. }
  208. msglet.innerHTML = `<span>${flair}</span> <span class='msglet-message'>${message}</span> <span class='msglet-count'><span>`
  209. msglet_wrapper.appendChild(msglet)
  210. console_msg_div.prepend(msglet_wrapper)
  211. slide_show(msglet_wrapper)
  212. // Calculate the time left of the message
  213. let lifespan = Math.min(1000 + message.length * 100, 5000)
  214. // Generate a progress bar
  215. let progress_bar = document.createElement('div')
  216. progress_bar.classList.add('console-message-wrapper-progress-bar')
  217. let progress = document.createElement('div')
  218. progress.classList.add('console-message-wrapper-progress')
  219. progress_bar.appendChild(progress)
  220. msglet.appendChild(progress_bar)
  221. progress.style.transitionDuration = lifespan + 'ms'
  222. // Animate the progress bar
  223. setTimeout(() => {
  224. progress.style.transform = 'scaleX(0)'
  225. }, 100)
  226. // Easeout the message after few seconds
  227. progress.addEventListener('transitionend', () => {
  228. if (progress.style.transform === 'scaleX(0)') {
  229. exit_to_right(msglet_wrapper)
  230. }
  231. })
  232. }
  233.  
  234. // Monkey Patch
  235. if (window.location.hostname !== 'localhost') { // Only works on host other than localhost
  236. console.log = (...args) => console_call('log', original_console_log, ...args)
  237. console.error = (...args) => console_call('error', original_console_error, ...args)
  238. console.warn = (...args) => console_call('warn', original_console_warn, ...args)
  239. console.info = (...args) => console_call('info', original_console_info, ...args)
  240. console.debug = (...args) => console_call('debug', original_console_debug, ...args)
  241. }
  242. })();