From ba46d54a7decbd7e94825e272ee4d06342a9aacb Mon Sep 17 00:00:00 2001 From: nekowinston Date: Tue, 5 Dec 2023 22:34:03 +0100 Subject: [PATCH] fix: color maths, error in `shade()` function (#240) --- nix/yarn-project.nix | 2 ++ package.json | 2 ++ src/theme/extensions/gitlens.ts | 8 +++--- src/theme/ui/brackets.ts | 19 ++++++------- src/theme/uiColors.ts | 8 +++--- src/theme/utils.ts | 48 ++++++++++----------------------- tsconfig.json | 1 + yarn.lock | 16 +++++++++++ 8 files changed, 53 insertions(+), 51 deletions(-) diff --git a/nix/yarn-project.nix b/nix/yarn-project.nix index 1684aad3..d993b545 100644 --- a/nix/yarn-project.nix +++ b/nix/yarn-project.nix @@ -498,6 +498,7 @@ cacheEntries = { "@types/semver@npm:7.5.1" = { filename = "@types-semver-npm-7.5.1-cda240741c-8e19822a2f.zip"; sha512 = "8e19822a2f6282785f4787b3640a205161a65f85de3d2159c5077002adb6e90b2f80bb7c8324ffdb9643061763e1672747f04b0ef3e9ac4179de0dce20ad641d"; }; "@types/send@npm:0.17.1" = { filename = "@types-send-npm-0.17.1-5f715ca966-6420837887.zip"; sha512 = "6420837887858f7aa82f2c0272f73edb42385bd0978f43095e83590a405d86c8cc6d918c30b2d542f1d8bddc9f3d16c2e8fdfca936940de71b97c45f228d1896"; }; "@types/serve-static@npm:1.15.2" = { filename = "@types-serve-static-npm-1.15.2-fc398c0cea-d5f8f5aaa7.zip"; sha512 = "d5f8f5aaa765be6417aa3f2ebe36591f4e9d2d8a7480edf7d3db041427420fd565cb921fc021271098dd2afafce2b443fc0d978faa3ae21a2a58ebde7d525e9e"; }; +"@types/tinycolor2@npm:1.4.6" = { filename = "@types-tinycolor2-npm-1.4.6-62a2039a99-a662fd1771.zip"; sha512 = "a662fd177135d27779b7ccba39881a85167f969787c71c7bf51903d288a519ad5b9526e0a4af7bde6444df6b7abe88bae893d569c6d804108c03dfec9509bdf6"; }; "@types/unist@npm:2.0.8" = { filename = "@types-unist-npm-2.0.8-c7fd7db3e3-f4852d10a6.zip"; sha512 = "f4852d10a6752dc70df363917ef74453e5d2fd42824c0f6d09d19d530618e1402193977b1207366af4415aaec81d4e262c64d00345402020c4ca179216e553c7"; }; "@types/unist@npm:3.0.0" = { filename = "@types-unist-npm-3.0.0-6eebd0c9a3-e9d21a8fb5.zip"; sha512 = "e9d21a8fb5e332be0acef29192d82632875b2ef3e700f1bc64fdfc1520189542de85c3d4f3bcfbc2f4afdb210f4c23f68061f3fbf10744e920d4f18430d19f49"; }; "@types/vscode@npm:1.80.0" = { filename = "@types-vscode-npm-1.80.0-7ea3286e88-ea2285698a.zip"; sha512 = "ea2285698a0a71d74436aea289e011e15099c9eb9637048bf5f82365ce5d0013dd064da61e82e6194774d212817c9931ebffe1055db1be88ebeba5683b062adb"; }; @@ -1238,6 +1239,7 @@ cacheEntries = { "through2@npm:2.0.5" = { filename = "through2-npm-2.0.5-77d90f13cd-cd71f7dcdc.zip"; sha512 = "cd71f7dcdc7a8204fea003a14a433ef99384b7d4e31f5497e1f9f622b3cf3be3691f908455f98723bdc80922a53af7fa10c3b7abbe51c6fd3d536dbc7850e2c4"; }; "timers-ext@npm:0.1.7" = { filename = "timers-ext-npm-0.1.7-7edcefbfb5-a8fffe2841.zip"; sha512 = "a8fffe2841ed6c3b16b2e72522ee46537c6a758294da45486c7e8ca52ff065931dd023c9f9946b87a13f48ae3dafe12678ab1f9d1ef24b6aea465762e0ffdcae"; }; "tiny-invariant@npm:1.3.1" = { filename = "tiny-invariant-npm-1.3.1-a8ca214e49-872dbd1ff2.zip"; sha512 = "872dbd1ff20a21303a2fd20ce3a15602cfa7fcf9b228bd694a52e2938224313b5385a1078cb667ed7375d1612194feaca81c4ecbe93121ca1baebe344de4f84c"; }; +"tinycolor2@npm:1.6.0" = { filename = "tinycolor2-npm-1.6.0-8df41252c6-066c3acf4f.zip"; sha512 = "066c3acf4f82b81c58a0d3ab85f49407efe95ba87afc3c7a16b1d77625193dfbe10dd46c26d0a263c1137361dd5a6a68bff2fb71def5fb9b9aec940fb030bcd4"; }; "titleize@npm:3.0.0" = { filename = "titleize-npm-3.0.0-7deac2f3a3-71fbbeabbf.zip"; sha512 = "71fbbeabbfb36ccd840559f67f21e356e1d03da2915b32d2ae1a60ddcc13a124be2739f696d2feb884983441d159a18649e8d956648d591bdad35c430a6b6d28"; }; "tmp@npm:0.2.1" = { filename = "tmp-npm-0.2.1-a9c8d9c0ca-445148d72d.zip"; sha512 = "445148d72df3ce99356bc89a7857a0c5c3b32958697a14e50952c6f7cf0a8016e746ababe9a74c1aa52f04c526661992f14659eba34d3c6701d49ba2f3cf781b"; }; "tmpl@npm:1.0.5" = { filename = "tmpl-npm-1.0.5-d399ba37e2-cd922d9b85.zip"; sha512 = "cd922d9b853c00fe414c5a774817be65b058d54a2d01ebb415840960406c669a0fc632f66df885e24cb022ec812739199ccbdb8d1164c3e513f85bfca5ab2873"; }; diff --git a/package.json b/package.json index 3576fa8e..a080e767 100644 --- a/package.json +++ b/package.json @@ -168,6 +168,7 @@ "@storybook/react": "^7.5.3", "@storybook/react-vite": "^7.5.3", "@types/node": "^18.18.13", + "@types/tinycolor2": "^1", "@types/vscode": "~1.80.0", "@typescript-eslint/eslint-plugin": "^6.13.0", "@typescript-eslint/parser": "^6.13.0", @@ -185,6 +186,7 @@ "react-dom": "^18.2.0", "shikiji": "^0.7.4", "storybook": "^7.5.3", + "tinycolor2": "^1.6.0", "tsup": "^8.0.1", "tsx": "^4.6.0", "type-flag": "^3.0.0", diff --git a/src/theme/extensions/gitlens.ts b/src/theme/extensions/gitlens.ts index 0dcdfffa..187269b6 100644 --- a/src/theme/extensions/gitlens.ts +++ b/src/theme/extensions/gitlens.ts @@ -93,14 +93,14 @@ export default function colors(context: ThemeContext): Partial { "gitlens.graphChangesColumnDeletedColor": palette.red, "gitlens.graphMinimapMarkerHeadColor": palette.green, "gitlens.graphScrollMarkerHeadColor": palette.green, - "gitlens.graphMinimapMarkerUpstreamColor": shade(palette.green, -0.5), - "gitlens.graphScrollMarkerUpstreamColor": shade(palette.green, -0.5), + "gitlens.graphMinimapMarkerUpstreamColor": shade(palette.green, -0.05), + "gitlens.graphScrollMarkerUpstreamColor": shade(palette.green, -0.05), "gitlens.graphMinimapMarkerHighlightsColor": palette.yellow, "gitlens.graphScrollMarkerHighlightsColor": palette.yellow, "gitlens.graphMinimapMarkerLocalBranchesColor": palette.blue, "gitlens.graphScrollMarkerLocalBranchesColor": palette.blue, - "gitlens.graphMinimapMarkerRemoteBranchesColor": shade(palette.blue, -0.5), - "gitlens.graphScrollMarkerRemoteBranchesColor": shade(palette.blue, -0.5), + "gitlens.graphMinimapMarkerRemoteBranchesColor": shade(palette.blue, -0.05), + "gitlens.graphScrollMarkerRemoteBranchesColor": shade(palette.blue, -0.05), "gitlens.graphMinimapMarkerStashesColor": palette.mauve, "gitlens.graphScrollMarkerStashesColor": palette.mauve, "gitlens.graphMinimapMarkerTagsColor": palette.flamingo, diff --git a/src/theme/ui/brackets.ts b/src/theme/ui/brackets.ts index 6ae87591..46a43b50 100644 --- a/src/theme/ui/brackets.ts +++ b/src/theme/ui/brackets.ts @@ -14,8 +14,9 @@ type BracketHLs = keyof PickStartsWith< const brackets = (context: ThemeContext): Record => { const { isLatte, options, palette } = context; - // invert the shade if current theme is latte - const L = isLatte ? -1 : 1; + // invert the shade of dimmed brackets if current theme is latte + const dimAmount = -0.07 * (isLatte ? -1 : 1); + const styles = { rainbow: { "editorBracketHighlight.foreground1": palette.red, @@ -27,15 +28,15 @@ const brackets = (context: ThemeContext): Record => { "editorBracketHighlight.unexpectedBracket.foreground": palette.maroon, }, dimmed: { - "editorBracketHighlight.foreground1": shade(palette.red, -0.6 * L), - "editorBracketHighlight.foreground2": shade(palette.peach, -0.6 * L), - "editorBracketHighlight.foreground3": shade(palette.yellow, -0.6 * L), - "editorBracketHighlight.foreground4": shade(palette.green, -0.6 * L), - "editorBracketHighlight.foreground5": shade(palette.sapphire, -0.6 * L), - "editorBracketHighlight.foreground6": shade(palette.mauve, -0.6 * L), + "editorBracketHighlight.foreground1": shade(palette.red, dimAmount), + "editorBracketHighlight.foreground2": shade(palette.peach, dimAmount), + "editorBracketHighlight.foreground3": shade(palette.yellow, dimAmount), + "editorBracketHighlight.foreground4": shade(palette.green, dimAmount), + "editorBracketHighlight.foreground5": shade(palette.sapphire, dimAmount), + "editorBracketHighlight.foreground6": shade(palette.mauve, dimAmount), "editorBracketHighlight.unexpectedBracket.foreground": shade( palette.maroon, - -0.6 * L, + dimAmount, ), }, monochromatic: { diff --git a/src/theme/uiColors.ts b/src/theme/uiColors.ts index 483a0b01..597e040b 100644 --- a/src/theme/uiColors.ts +++ b/src/theme/uiColors.ts @@ -67,10 +67,10 @@ export const getUiColors = ( "button.foreground": palette.crust, "button.border": transparent, "button.separator": transparent, - "button.hoverBackground": shade(accent, 0.2), + "button.hoverBackground": shade(accent, 0.07), "button.secondaryForeground": palette.text, "button.secondaryBackground": palette.surface2, - "button.secondaryHoverBackground": shade(palette.surface2, 0.2), + "button.secondaryHoverBackground": shade(palette.surface2, 0.07), "checkbox.background": palette.surface1, "checkbox.border": border, "checkbox.foreground": accent, @@ -212,7 +212,7 @@ export const getUiColors = ( "extensionButton.prominentForeground": palette.crust, "extensionButton.prominentBackground": accent, "extensionButton.separator": palette.base, - "extensionButton.prominentHoverBackground": shade(accent, 0.2), + "extensionButton.prominentHoverBackground": shade(accent, 0.07), "extensionBadge.remoteBackground": palette.blue, "extensionBadge.remoteForeground": palette.crust, "extensionIcon.starForeground": palette.yellow, @@ -408,7 +408,7 @@ export const getUiColors = ( "tab.activeForeground": accent, "tab.activeModifiedBorder": palette.yellow, "tab.border": palette.mantle, - "tab.hoverBackground": shade(palette.base, 0.1), + "tab.hoverBackground": shade(palette.base, 0.05), "tab.hoverBorder": transparent, "tab.hoverForeground": accent, "tab.inactiveBackground": palette.mantle, diff --git a/src/theme/utils.ts b/src/theme/utils.ts index 4768475f..b3df6b23 100644 --- a/src/theme/utils.ts +++ b/src/theme/utils.ts @@ -1,52 +1,32 @@ +import tinycolor from "tinycolor2"; + /** * @param {string} s String to capitalize * @returns {string} returns the String with the first character uppercased. */ -export const capitalize = (s: string): string => { - return s.charAt(0).toUpperCase() + s.substring(1); -}; +export const capitalize = (s: string): string => + s.charAt(0).toUpperCase() + s.substring(1); /** * @param {string} color 6-character hex color, like "#FF69B4". * @param {number} opacity how much opacity to apply. value between 0 and 1. * @returns {string} color with the specified opacity, hex-encoded. */ -export const opacity = (color: string, opacity: number): string => { - opacity = Math.floor(255 * opacity); - color = color.replace(`#`, ``); - - if (color.length === 6) { - return `#${color}${opacity.toString(16)}`; - } else { - return color; - } -}; +export const opacity = (color: string, opacity: number): string => + tinycolor(color).setAlpha(opacity).toHex8String(); /** * @param {string} color 6-character hex color, like "#FF69B4". * @param {number} magnitude represents the magnitude by which hexColor should be lightened or darkened. value between 0 and 1. * @returns {string} returns the lightened or darkened color. */ -// adapted from https://natclark.com/tutorials/javascript-lighten-darken-hex-color/ -export const shade = (color: string, magnitude: number): string => { - magnitude = Math.round(magnitude * 100); - color = color.replace(`#`, ``); - - if (color.length === 6) { - const decimalColor = parseInt(color, 16); - let r = (decimalColor >> 16) + magnitude; - r > 255 && (r = 255); - r < 0 && (r = 0); - let g = (decimalColor & 0x0000ff) + magnitude; - g > 255 && (g = 255); - g < 0 && (g = 0); - let b = ((decimalColor >> 8) & 0x00ff) + magnitude; - b > 255 && (b = 255); - b < 0 && (b = 0); - return `#${(g | (b << 8) | (r << 16)).toString(16)}`; - } else { - return color; - } -}; +export const shade = (color: string, magnitude: number): string => + magnitude > 0 + ? tinycolor(color) + .lighten(magnitude * 100) + .toHexString() + : tinycolor(color) + .darken(Math.abs(magnitude * 100)) + .toHexString(); export const transparent = "#00000000"; diff --git a/tsconfig.json b/tsconfig.json index f75a48e8..f71b771c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "module": "commonjs", + "esModuleInterop": true, "target": "es6", "outDir": "./dist", "sourceMap": false, diff --git a/yarn.lock b/yarn.lock index db73a3f2..1dcd3e7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4572,6 +4572,13 @@ __metadata: languageName: node linkType: hard +"@types/tinycolor2@npm:^1": + version: 1.4.6 + resolution: "@types/tinycolor2@npm:1.4.6" + checksum: a662fd177135d27779b7ccba39881a85167f969787c71c7bf51903d288a519ad5b9526e0a4af7bde6444df6b7abe88bae893d569c6d804108c03dfec9509bdf6 + languageName: node + linkType: hard + "@types/unist@npm:*, @types/unist@npm:^3.0.0": version: 3.0.0 resolution: "@types/unist@npm:3.0.0" @@ -5544,6 +5551,7 @@ __metadata: "@storybook/react": "npm:^7.5.3" "@storybook/react-vite": "npm:^7.5.3" "@types/node": "npm:^18.18.13" + "@types/tinycolor2": "npm:^1" "@types/vscode": "npm:~1.80.0" "@typescript-eslint/eslint-plugin": "npm:^6.13.0" "@typescript-eslint/parser": "npm:^6.13.0" @@ -5561,6 +5569,7 @@ __metadata: react-dom: "npm:^18.2.0" shikiji: "npm:^0.7.4" storybook: "npm:^7.5.3" + tinycolor2: "npm:^1.6.0" tsup: "npm:^8.0.1" tsx: "npm:^4.6.0" type-flag: "npm:^3.0.0" @@ -11837,6 +11846,13 @@ __metadata: languageName: node linkType: hard +"tinycolor2@npm:^1.6.0": + version: 1.6.0 + resolution: "tinycolor2@npm:1.6.0" + checksum: 066c3acf4f82b81c58a0d3ab85f49407efe95ba87afc3c7a16b1d77625193dfbe10dd46c26d0a263c1137361dd5a6a68bff2fb71def5fb9b9aec940fb030bcd4 + languageName: node + linkType: hard + "titleize@npm:^3.0.0": version: 3.0.0 resolution: "titleize@npm:3.0.0"