mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08: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