mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 02:46:04 +01:00 
			
		
		
		
	Always pass 6-digit hex color to monaco (#25780)
Monaco can not deal with color formats other than 6-digit hex, so we convert the colors for it via new [`tinycolor2`](https://github.com/bgrins/TinyColor) dependency (5kB minzipped). Also, with the addition of the module, we can replace the existing `hexToRGBColor` usage, I verified it is compatible with the current tests before removing the function. Fixes: https://github.com/go-gitea/gitea/issues/25770
This commit is contained in:
		
							
								
								
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -41,6 +41,7 @@ | |||||||
|         "sortablejs": "1.15.0", |         "sortablejs": "1.15.0", | ||||||
|         "swagger-ui-dist": "5.1.0", |         "swagger-ui-dist": "5.1.0", | ||||||
|         "throttle-debounce": "5.0.0", |         "throttle-debounce": "5.0.0", | ||||||
|  |         "tinycolor2": "1.6.0", | ||||||
|         "tippy.js": "6.3.7", |         "tippy.js": "6.3.7", | ||||||
|         "toastify-js": "1.12.0", |         "toastify-js": "1.12.0", | ||||||
|         "tributejs": "5.1.3", |         "tributejs": "5.1.3", | ||||||
| @@ -10265,6 +10266,11 @@ | |||||||
|       "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", |       "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", | ||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/tinycolor2": { | ||||||
|  |       "version": "1.6.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", | ||||||
|  |       "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" | ||||||
|  |     }, | ||||||
|     "node_modules/tinypool": { |     "node_modules/tinypool": { | ||||||
|       "version": "0.6.0", |       "version": "0.6.0", | ||||||
|       "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.6.0.tgz", |       "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.6.0.tgz", | ||||||
|   | |||||||
| @@ -40,6 +40,7 @@ | |||||||
|     "sortablejs": "1.15.0", |     "sortablejs": "1.15.0", | ||||||
|     "swagger-ui-dist": "5.1.0", |     "swagger-ui-dist": "5.1.0", | ||||||
|     "throttle-debounce": "5.0.0", |     "throttle-debounce": "5.0.0", | ||||||
|  |     "tinycolor2": "1.6.0", | ||||||
|     "tippy.js": "6.3.7", |     "tippy.js": "6.3.7", | ||||||
|     "toastify-js": "1.12.0", |     "toastify-js": "1.12.0", | ||||||
|     "tributejs": "5.1.3", |     "tributejs": "5.1.3", | ||||||
|   | |||||||
| @@ -26,7 +26,8 @@ | |||||||
| <script> | <script> | ||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import {SvgIcon} from '../svg.js'; | import {SvgIcon} from '../svg.js'; | ||||||
| import {useLightTextOnBackground, hexToRGBColor} from '../utils/color.js'; | import {useLightTextOnBackground} from '../utils/color.js'; | ||||||
|  | import tinycolor from 'tinycolor2'; | ||||||
|  |  | ||||||
| const {appSubUrl, i18n} = window.config; | const {appSubUrl, i18n} = window.config; | ||||||
|  |  | ||||||
| @@ -77,7 +78,7 @@ export default { | |||||||
|     labels() { |     labels() { | ||||||
|       return this.issue.labels.map((label) => { |       return this.issue.labels.map((label) => { | ||||||
|         let textColor; |         let textColor; | ||||||
|         const [r, g, b] = hexToRGBColor(label.color); |         const {r, g, b} = tinycolor(label.color).toRgb(); | ||||||
|         if (useLightTextOnBackground(r, g, b)) { |         if (useLightTextOnBackground(r, g, b)) { | ||||||
|           textColor = '#eeeeee'; |           textColor = '#eeeeee'; | ||||||
|         } else { |         } else { | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | import tinycolor from 'tinycolor2'; | ||||||
| import {basename, extname, isObject, isDarkTheme} from '../utils.js'; | import {basename, extname, isObject, isDarkTheme} from '../utils.js'; | ||||||
| import {onInputDebounce} from '../utils/dom.js'; | import {onInputDebounce} from '../utils/dom.js'; | ||||||
|  |  | ||||||
| @@ -69,33 +70,34 @@ export async function createMonaco(textarea, filename, editorOpts) { | |||||||
|   textarea.parentNode.append(container); |   textarea.parentNode.append(container); | ||||||
|  |  | ||||||
|   // https://github.com/microsoft/monaco-editor/issues/2427 |   // https://github.com/microsoft/monaco-editor/issues/2427 | ||||||
|  |   // also, monaco can only parse 6-digit hex colors, so we convert the colors to that format | ||||||
|   const styles = window.getComputedStyle(document.documentElement); |   const styles = window.getComputedStyle(document.documentElement); | ||||||
|   const getProp = (name) => styles.getPropertyValue(name).trim(); |   const getColor = (name) => tinycolor(styles.getPropertyValue(name).trim()).toString('hex6'); | ||||||
|  |  | ||||||
|   monaco.editor.defineTheme('gitea', { |   monaco.editor.defineTheme('gitea', { | ||||||
|     base: isDarkTheme() ? 'vs-dark' : 'vs', |     base: isDarkTheme() ? 'vs-dark' : 'vs', | ||||||
|     inherit: true, |     inherit: true, | ||||||
|     rules: [ |     rules: [ | ||||||
|       { |       { | ||||||
|         background: getProp('--color-code-bg'), |         background: getColor('--color-code-bg'), | ||||||
|       } |       } | ||||||
|     ], |     ], | ||||||
|     colors: { |     colors: { | ||||||
|       'editor.background': getProp('--color-code-bg'), |       'editor.background': getColor('--color-code-bg'), | ||||||
|       'editor.foreground': getProp('--color-text'), |       'editor.foreground': getColor('--color-text'), | ||||||
|       'editor.inactiveSelectionBackground': getProp('--color-primary-light-4'), |       'editor.inactiveSelectionBackground': getColor('--color-primary-light-4'), | ||||||
|       'editor.lineHighlightBackground': getProp('--color-editor-line-highlight'), |       'editor.lineHighlightBackground': getColor('--color-editor-line-highlight'), | ||||||
|       'editor.selectionBackground': getProp('--color-primary-light-3'), |       'editor.selectionBackground': getColor('--color-primary-light-3'), | ||||||
|       'editor.selectionForeground': getProp('--color-primary-light-3'), |       'editor.selectionForeground': getColor('--color-primary-light-3'), | ||||||
|       'editorLineNumber.background': getProp('--color-code-bg'), |       'editorLineNumber.background': getColor('--color-code-bg'), | ||||||
|       'editorLineNumber.foreground': getProp('--color-secondary-dark-6'), |       'editorLineNumber.foreground': getColor('--color-secondary-dark-6'), | ||||||
|       'editorWidget.background': getProp('--color-body'), |       'editorWidget.background': getColor('--color-body'), | ||||||
|       'editorWidget.border': getProp('--color-secondary'), |       'editorWidget.border': getColor('--color-secondary'), | ||||||
|       'input.background': getProp('--color-input-background'), |       'input.background': getColor('--color-input-background'), | ||||||
|       'input.border': getProp('--color-input-border'), |       'input.border': getColor('--color-input-border'), | ||||||
|       'input.foreground': getProp('--color-input-text'), |       'input.foreground': getColor('--color-input-text'), | ||||||
|       'scrollbar.shadow': getProp('--color-shadow'), |       'scrollbar.shadow': getColor('--color-shadow'), | ||||||
|       'progressBar.background': getProp('--color-primary'), |       'progressBar.background': getColor('--color-primary'), | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import {useLightTextOnBackground, hexToRGBColor} from '../utils/color.js'; | import {useLightTextOnBackground} from '../utils/color.js'; | ||||||
|  | import tinycolor from 'tinycolor2'; | ||||||
|  |  | ||||||
| const {csrfToken} = window.config; | const {csrfToken} = window.config; | ||||||
|  |  | ||||||
| @@ -210,7 +211,7 @@ export function initRepoProject() { | |||||||
| } | } | ||||||
|  |  | ||||||
| function setLabelColor(label, color) { | function setLabelColor(label, color) { | ||||||
|   const [r, g, b] = hexToRGBColor(color); |   const {r, g, b} = tinycolor(color).toRgb(); | ||||||
|   if (useLightTextOnBackground(r, g, b)) { |   if (useLightTextOnBackground(r, g, b)) { | ||||||
|     label.removeClass('dark-label').addClass('light-label'); |     label.removeClass('dark-label').addClass('light-label'); | ||||||
|   } else { |   } else { | ||||||
|   | |||||||
| @@ -13,27 +13,6 @@ function getLuminance(r, g, b) { | |||||||
|   return 0.2126 * R + 0.7152 * G + 0.0722 * B; |   return 0.2126 * R + 0.7152 * G + 0.0722 * B; | ||||||
| } | } | ||||||
|  |  | ||||||
| // Get color as RGB values in 0..255 range from the hex color string (with or without #) |  | ||||||
| export function hexToRGBColor(backgroundColorStr) { |  | ||||||
|   let backgroundColor = backgroundColorStr; |  | ||||||
|   if (backgroundColorStr[0] === '#') { |  | ||||||
|     backgroundColor = backgroundColorStr.substring(1); |  | ||||||
|   } |  | ||||||
|   // only support transfer of rgb, rgba, rrggbb and rrggbbaa |  | ||||||
|   // if not in these formats, use default values 0, 0, 0 |  | ||||||
|   if (![3, 4, 6, 8].includes(backgroundColor.length)) { |  | ||||||
|     return [0, 0, 0]; |  | ||||||
|   } |  | ||||||
|   if ([3, 4].includes(backgroundColor.length)) { |  | ||||||
|     const [r, g, b] = backgroundColor; |  | ||||||
|     backgroundColor = `${r}${r}${g}${g}${b}${b}`; |  | ||||||
|   } |  | ||||||
|   const r = parseInt(backgroundColor.substring(0, 2), 16); |  | ||||||
|   const g = parseInt(backgroundColor.substring(2, 4), 16); |  | ||||||
|   const b = parseInt(backgroundColor.substring(4, 6), 16); |  | ||||||
|   return [r, g, b]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Reference from: https://firsching.ch/github_labels.html | // Reference from: https://firsching.ch/github_labels.html | ||||||
| // In the future WCAG 3 APCA may be a better solution. | // In the future WCAG 3 APCA may be a better solution. | ||||||
| // Check if text should use light color based on RGB of background | // Check if text should use light color based on RGB of background | ||||||
|   | |||||||
| @@ -1,17 +1,5 @@ | |||||||
| import {test, expect} from 'vitest'; | import {test, expect} from 'vitest'; | ||||||
| import {hexToRGBColor, useLightTextOnBackground} from './color.js'; | import {useLightTextOnBackground} from './color.js'; | ||||||
|  |  | ||||||
| test('hexToRGBColor', () => { |  | ||||||
|   expect(hexToRGBColor('2b8685')).toEqual([43, 134, 133]); |  | ||||||
|   expect(hexToRGBColor('1e1')).toEqual([17, 238, 17]); |  | ||||||
|   expect(hexToRGBColor('#1e1')).toEqual([17, 238, 17]); |  | ||||||
|   expect(hexToRGBColor('1e16')).toEqual([17, 238, 17]); |  | ||||||
|   expect(hexToRGBColor('3bb6b3')).toEqual([59, 182, 179]); |  | ||||||
|   expect(hexToRGBColor('#3bb6b399')).toEqual([59, 182, 179]); |  | ||||||
|   expect(hexToRGBColor('#0')).toEqual([0, 0, 0]); |  | ||||||
|   expect(hexToRGBColor('#00000')).toEqual([0, 0, 0]); |  | ||||||
|   expect(hexToRGBColor('#1234567')).toEqual([0, 0, 0]); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| test('useLightTextOnBackground', () => { | test('useLightTextOnBackground', () => { | ||||||
|   expect(useLightTextOnBackground(215, 58, 74)).toBe(true); |   expect(useLightTextOnBackground(215, 58, 74)).toBe(true); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user