Harmonize terminal colors with accent

Meaningful terminal colors
This commit is contained in:
midn8hustlr
2024-04-09 04:59:01 +05:30
parent 4db397322f
commit 0ad978ae2b
5 changed files with 185 additions and 117 deletions
@@ -6,11 +6,14 @@ if [ $# -eq 0 ]; then
exit 1 exit 1
fi fi
# check if the file ~/.cache/ags/user/colormode.txt exists. if not, create it. else, read it to $lightdark
colormodefile="$HOME/.cache/ags/user/colormode.txt" colormodefile="$HOME/.cache/ags/user/colormode.txt"
lightdark="dark" lightdark="dark"
transparency="opaque" transparency="opaque"
materialscheme="vibrant" materialscheme="vibrant"
terminalscheme="$HOME/.config/ags/scripts/templates/terminal/scheme-gruvbox.json"
#terminalscheme="$HOME/.config/ags/scripts/templates/terminal/scheme-catppuccin.json"
#terminalscheme="$HOME/.config/ags/scripts/templates/terminal/scheme-vscode.json"
if [ ! -f $colormodefile ]; then if [ ! -f $colormodefile ]; then
echo "dark" > $colormodefile echo "dark" > $colormodefile
echo "opaque" >> $colormodefile echo "opaque" >> $colormodefile
@@ -35,6 +38,7 @@ cd "$HOME/.config/ags/scripts/" || exit
if [[ "$1" = "#"* ]]; then # this is a color if [[ "$1" = "#"* ]]; then # this is a color
color_generation/generate_colors_material.py --color "$1" \ color_generation/generate_colors_material.py --color "$1" \
--mode "$lightdark" --scheme "$materialscheme" --transparency "$transparency" \ --mode "$lightdark" --scheme "$materialscheme" --transparency "$transparency" \
--termscheme $terminalscheme --blend_bg_fg \
> "$HOME"/.cache/ags/user/generated/material_colors.scss > "$HOME"/.cache/ags/user/generated/material_colors.scss
if [ "$2" = "--apply" ]; then if [ "$2" = "--apply" ]; then
cp "$HOME"/.cache/ags/user/generated/material_colors.scss "$HOME/.config/ags/scss/_material.scss" cp "$HOME"/.cache/ags/user/generated/material_colors.scss "$HOME/.config/ags/scss/_material.scss"
@@ -46,7 +50,9 @@ elif [ "$backend" = "material" ]; then
smartflag='--smart True' smartflag='--smart True'
fi fi
color_generation/generate_colors_material.py --path "$1" \ color_generation/generate_colors_material.py --path "$1" \
--mode "$lightdark" --scheme "$materialscheme" --transparency "$transparency" --cache "$HOME/.cache/ags/user/color.txt" $smartflag \ --mode "$lightdark" --scheme "$materialscheme" --transparency "$transparency" \
--termscheme $terminalscheme --blend_bg_fg \
--cache "$HOME/.cache/ags/user/color.txt" $smartflag \
> "$HOME"/.cache/ags/user/generated/material_colors.scss > "$HOME"/.cache/ags/user/generated/material_colors.scss
if [ "$2" = "--apply" ]; then if [ "$2" = "--apply" ]; then
cp "$HOME"/.cache/ags/user/generated/material_colors.scss "$HOME/.config/ags/scss/_material.scss" cp "$HOME"/.cache/ags/user/generated/material_colors.scss "$HOME/.config/ags/scss/_material.scss"
@@ -1,34 +1,37 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import math import math
import json
from PIL import Image from PIL import Image
from materialyoucolor.quantize import QuantizeCelebi from materialyoucolor.quantize import QuantizeCelebi
from materialyoucolor.score.score import Score from materialyoucolor.score.score import Score
from materialyoucolor.hct import Hct from materialyoucolor.hct import Hct
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
from materialyoucolor.utils.color_utils import rgba_from_argb, argb_from_rgb from materialyoucolor.utils.color_utils import (rgba_from_argb, argb_from_rgb, argb_from_rgba)
from materialyoucolor.utils.math_utils import (sanitize_degrees_double, difference_degrees, rotation_direction)
argb_to_hex = lambda argb: "#{:02X}{:02X}{:02X}".format(*map(round, rgba_from_argb(argb)))
hex_to_argb = lambda hex_code: argb_from_rgb(int(hex_code[1:3], 16), int(hex_code[3:5], 16), int(hex_code[5:], 16))
parser = argparse.ArgumentParser(description='Color generation script') parser = argparse.ArgumentParser(description='Color generation script')
parser.add_argument('--path', type=str, default=None, help='generate colorscheme from image') parser.add_argument('--path', type=str, default=None, help='generate colorscheme from image')
parser.add_argument('--size', type=int , default=128 , help='bitmap image size') parser.add_argument('--size', type=int , default=128 , help='bitmap image size')
parser.add_argument('--color', type=str, default=None, help='generate colorscheme from color') parser.add_argument('--color', type=str, default=None, help='generate colorscheme from color')
parser.add_argument('--mode', type=str, choices=['dark', 'light'], default='dark', help='dark or light mode') parser.add_argument('--mode', type=str, choices=['dark', 'light'], default='dark', help='dark or light mode')
parser.add_argument('--scheme', type=str, default=None, help='material scheme to use') parser.add_argument('--scheme', type=str, default='vibrant', help='material scheme to use')
parser.add_argument('--smart', type=str, default=False, help='decide scheme type based on image color') parser.add_argument('--smart', action='store_true', default=False, help='decide scheme type based on image color')
parser.add_argument('--transparency', type=str, choices=['opaque', 'transparent'], default='opaque', help='enable transparency') parser.add_argument('--transparency', type=str, choices=['opaque', 'transparent'], default='opaque', help='enable transparency')
parser.add_argument('--termscheme', type=str, default=None, help='JSON file containg the terminal scheme for generating term colors')
parser.add_argument('--harmony', type=float , default=0.5, help='(0-1) Color hue shift towards accent')
parser.add_argument('--harmonize_threshold', type=float , default=35, help='(0-180) Max threshold angle to limit color hue shift')
parser.add_argument('--blend_bg_fg', action='store_true', default=False, help='Shift terminal background or foreground towards accent')
parser.add_argument('--cache', type=str, default=None, help='file path to store the generated color') parser.add_argument('--cache', type=str, default=None, help='file path to store the generated color')
parser.add_argument('--debug', action='store_true', default=False, help='debug mode') parser.add_argument('--debug', action='store_true', default=False, help='debug mode')
args = parser.parse_args() args = parser.parse_args()
darkmode = (args.mode == 'dark') rgba_to_hex = lambda rgba: "#{:02X}{:02X}{:02X}".format(rgba[0], rgba[1], rgba[2])
transparent = (args.transparency == 'transparent') argb_to_hex = lambda argb: "#{:02X}{:02X}{:02X}".format(*map(round, rgba_from_argb(argb)))
print(f"$darkmode: {darkmode};") hex_to_argb = lambda hex_code: argb_from_rgb(int(hex_code[1:3], 16), int(hex_code[3:5], 16), int(hex_code[5:], 16))
print(f"$transparent: {transparent};") display_color = lambda rgba : "\x1B[38;2;{};{};{}m{}\x1B[0m".format(rgba[0], rgba[1], rgba[2], "\x1b[7m \x1b[7m")
def calculate_optimal_size (width, height, bitmap_size): def calculate_optimal_size (width: int, height: int, bitmap_size: int) -> (int, int):
image_area = width * height; image_area = width * height;
bitmap_area = bitmap_size ** 2 bitmap_area = bitmap_size ** 2
scale = math.sqrt(bitmap_area/image_area) if image_area > bitmap_area else 1 scale = math.sqrt(bitmap_area/image_area) if image_area > bitmap_area else 1
@@ -40,6 +43,23 @@ def calculate_optimal_size (width, height, bitmap_size):
new_height = 1 new_height = 1
return new_width, new_height return new_width, new_height
def harmonize (design_color: int, source_color: int, threshold: float = 35, harmony: float = 0.5) -> int:
from_hct = Hct.from_int(design_color)
to_hct = Hct.from_int(source_color)
difference_degrees_ = difference_degrees(from_hct.hue, to_hct.hue)
rotation_degrees = min(difference_degrees_ * harmony, threshold)
output_hue = sanitize_degrees_double(
from_hct.hue + rotation_degrees * rotation_direction(from_hct.hue, to_hct.hue)
)
return Hct.from_hct(output_hue, from_hct.chroma, from_hct.tone).to_int()
def force_chroma_tone (argb: int, chroma: float = 0, tone: float = 0) -> int:
hct = Hct.from_int(argb)
return Hct.from_hct(hct.hue, chroma, tone).to_int()
darkmode = (args.mode == 'dark')
transparent = (args.transparency == 'transparent')
if args.path is not None: if args.path is not None:
image = Image.open(args.path) image = Image.open(args.path)
wsize, hsize = image.size wsize, hsize = image.size
@@ -62,55 +82,91 @@ elif args.color is not None:
argb = hex_to_argb(args.color) argb = hex_to_argb(args.color)
hct = Hct.from_int(argb) hct = Hct.from_int(argb)
# Default scheme -> Tonal Spot (Android Default) if args.scheme == 'fruitsalad':
from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant as Scheme from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad as Scheme
if args.scheme is not None: elif args.scheme == 'expressive':
if args.scheme == 'fruitsalad': from materialyoucolor.scheme.scheme_expressive import SchemeExpressive as Scheme
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad as Scheme elif args.scheme == 'monochrome':
elif args.scheme == 'expressive': from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome as Scheme
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive as Scheme elif args.scheme == 'rainbow':
elif args.scheme == 'monochrome': from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow as Scheme
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome as Scheme elif args.scheme == 'tonalspot':
elif args.scheme == 'rainbow': from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot as Scheme
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow as Scheme elif args.scheme == 'neutral':
elif args.scheme == 'tonalspot': from materialyoucolor.scheme.scheme_neutral import SchemeNeutral as Scheme
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot as Scheme elif args.scheme == 'fidelity':
elif args.scheme == 'neutral': from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity as Scheme
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral as Scheme elif args.scheme == 'content':
elif args.scheme == 'fidelity': from materialyoucolor.scheme.scheme_content import SchemeContent as Scheme
from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity as Scheme else:
elif args.scheme == 'content': from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant as Scheme
from materialyoucolor.scheme.scheme_content import SchemeContent as Scheme
# Generate # Generate
scheme = Scheme(hct, darkmode, 0.0) scheme = Scheme(hct, darkmode, 0.0)
material_colors = {}
term_colors = {}
for color in vars(MaterialDynamicColors).keys(): for color in vars(MaterialDynamicColors).keys():
color_name = getattr(MaterialDynamicColors, color) color_name = getattr(MaterialDynamicColors, color)
if hasattr(color_name, "get_hct"): if hasattr(color_name, "get_hct"):
rgba = color_name.get_hct(scheme).to_rgba() rgba = color_name.get_hct(scheme).to_rgba()
r, g, b, a = rgba material_colors[color] = rgba_to_hex(rgba)
hex_code = f"#{r:02X}{g:02X}{b:02X}"
print('$' + color + ': ' + hex_code + ';')
if args.debug == True: # Extended material
print('---------------------') if darkmode == True:
material_colors['success'] = '#B5CCBA'
material_colors['onSuccess'] = '#213528'
material_colors['successContainer'] = '#374B3E'
material_colors['onSuccessContainer'] = '#D1E9D6'
else:
material_colors['success'] = '#4F6354'
material_colors['onSuccess'] = '#FFFFFF'
material_colors['successContainer'] = '#D1E8D5'
material_colors['onSuccessContainer'] = '#0C1F13'
# Terminal Colors
if args.termscheme is not None:
with open(args.termscheme, 'r') as f:
json_termscheme = f.read()
term_source_colors = json.loads(json_termscheme)['dark' if darkmode else 'light']
for color, val in term_source_colors.items():
if args.blend_bg_fg:
tone_ovr = 10 if darkmode else 90
if args.blend_bg_fg and color == "term0":
harmonized = force_chroma_tone(argb, 10, tone_ovr)
elif args.blend_bg_fg and color == "term15":
harmonized = force_chroma_tone(argb, 10, 100 - tone_ovr)
else:
harmonized = harmonize(hex_to_argb(val), argb, args.harmonize_threshold, args.harmony)
term_colors[color] = argb_to_hex(harmonized)
if args.debug == False:
print(f"$darkmode: {darkmode};")
print(f"$transparent: {transparent};")
for color, code in material_colors.items():
print(f"${color}: {code};")
for color, code in term_colors.items():
print(f"${color}: {code};")
else:
if args.path is not None: if args.path is not None:
print('Image size: {} x {}'.format(wsize, hsize)) print('\n--------------Image properties-----------------')
print('Resized image: {} x {}'.format(wsize_new, hsize_new)) print(f"Image size: {wsize} x {hsize}")
print('Hue:', hct.hue) print(f"Resized image: {wsize_new} x {hsize_new}")
print('Chroma:', hct.chroma) print('\n---------------Selected color------------------')
print('Tone:', hct.tone) print(f"Dark mode: {darkmode}")
r, g, b, a = rgba_from_argb(argb) print(f"Scheme: {args.scheme}")
hex_code = argb_to_hex(argb) print(f"Accent color: {display_color(rgba_from_argb(argb))} {argb_to_hex(argb)}")
print('Selected Color:', "\x1B[38;2;{};{};{}m{}\x1B[0m".format(r, g, b, "\x1b[7m \x1b[7m"), hex_code) print(f"HCT: {hct.hue:.2f} {hct.chroma:.2f} {hct.tone:.2f}")
print('Dark mode:', darkmode) print('\n---------------Material colors-----------------')
print('Scheme:', args.scheme) for color, code in material_colors.items():
print('---------------------') rgba = rgba_from_argb(hex_to_argb(code))
for color in vars(MaterialDynamicColors).keys(): print(f"{color.ljust(32)} : {display_color(rgba)} {code}")
color_name = getattr(MaterialDynamicColors, color) print('\n----------Harmonize terminal colors------------')
if hasattr(color_name, "get_hct"): for color, code in term_colors.items():
rgba = color_name.get_hct(scheme).to_rgba() rgba = rgba_from_argb(hex_to_argb(code))
r, g, b, a = rgba code_source = term_source_colors[color]
hex_code = f"#{r:02X}{g:02X}{b:02X}" rgba_source = rgba_from_argb(hex_to_argb(code_source))
print(color.ljust(32), "\x1B[38;2;{};{};{}m{}\x1B[0m".format(rgba[0], rgba[1], rgba[2], "\x1b[7m \x1b[7m"), hex_code) print(f"{color.ljust(6)} : {display_color(rgba_source)} {code_source} --> {display_color(rgba)} {code}")
print('-----------------------------------------------')
@@ -1 +1 @@
]4;0;#$background #\]4;1;#$error #\]4;2;#$secondaryContainer #\]4;3;#$onSecondaryContainer #\]4;4;#$onSecondaryContainer #\]4;5;#$onSecondaryContainer #\]4;6;#$onSecondaryContainer #\]4;7;#$onSurfaceVariant #\]4;8;#$onSurfaceVariant #\]4;9;#$error #\]4;10;#$secondaryContainer #\]4;11;#$onSecondaryContainer #\]4;12;#$onSecondaryContainer #\]4;13;#$onSecondaryContainer #\]4;14;#$onSecondaryContainer #\]4;15;#$onSurfaceVariant #\]10;#$onSurfaceVariant #\]11;[$alpha]#$background #\]12;#$onSurfaceVariant #\]13;#$onSurfaceVariant #\]17;#$onSurfaceVariant #\]19;#$background #\]4;232;#$onSurfaceVariant #\]4;256;#$onSurfaceVariant #\]708;[$alpha]#$background #\ ]4;0;#$term0 #\]4;1;#$term1 #\]4;2;#$term2 #\]4;3;#$term3 #\]4;4;#$term4 #\]4;5;#$term5 #\]4;6;#$term6 #\]4;7;#$term7 #\]4;8;#$term8 #\]4;9;#$term9 #\]4;10;#$term10 #\]4;11;#$term11 #\]4;12;#$term12 #\]4;13;#$term13 #\]4;14;#$term14 #\]4;15;#$term15 #\]10;#$term7 #\]11;[$alpha]#$term0 #\]12;#$term7 #\]13;#$term7 #\]17;#$term7 #\]19;#$term0 #\]4;232;#$term7 #\]4;256;#$term7 #\]708;[$alpha]#$term0 #\
-14
View File
@@ -1,20 +1,6 @@
$rootTransparency: 0.31; // Transparency = 1 - opacity $rootTransparency: 0.31; // Transparency = 1 - opacity
$transparency: 0.8; $transparency: 0.8;
// Extended material
$success: #4f6354;
$onSuccess: #ffffff;
$successContainer: #d1e8d5;
$onSuccessContainer: #0c1f13;
@if $darkmode == True {
// Dark variant
$success: #b5ccba;
$onSuccess: #213528;
$successContainer: #374b3e;
$onSuccessContainer: #d1e9d6;
}
// Transparent versions // Transparent versions
@if $transparent == True { @if $transparent == True {
$background: transparentize($background, $rootTransparency); $background: transparentize($background, $rootTransparency);
+67 -47
View File
@@ -1,56 +1,76 @@
$darkmode: True; $darkmode: True;
$transparent: False; $transparent: False;
$primary_paletteKeyColor: #A504FF; $primary_paletteKeyColor: #57814C;
$secondary_paletteKeyColor: #8B6D8D; $secondary_paletteKeyColor: #6C7B65;
$tertiary_paletteKeyColor: #9A678C; $tertiary_paletteKeyColor: #527F82;
$neutral_paletteKeyColor: #7D7480; $neutral_paletteKeyColor: #747870;
$neutral_variant_paletteKeyColor: #7E7382; $neutral_variant_paletteKeyColor: #73796E;
$background: #17111B; $background: #11140F;
$onBackground: #EBDFED; $onBackground: #E1E4DA;
$surface: #17111B; $surface: #11140F;
$surfaceDim: #17111B; $surfaceDim: #11140F;
$surfaceBright: #3E3741; $surfaceBright: #363A34;
$surfaceContainerLowest: #FFFFFF; $surfaceContainerLowest: #0C0F0A;
$surfaceContainerLow: #1F1923; $surfaceContainerLow: #191D17;
$surfaceContainer: #241D27; $surfaceContainer: #1D211B;
$surfaceContainerHigh: #2E2832; $surfaceContainerHigh: #272B25;
$surfaceContainerHighest: #39323D; $surfaceContainerHighest: #32362F;
$onSurface: #EBDFED; $onSurface: #E1E4DA;
$surfaceVariant: #4D4351; $surfaceVariant: #43483F;
$onSurfaceVariant: #CFC2D3; $onSurfaceVariant: #C3C8BC;
$inverseSurface: #EBDFED; $inverseSurface: #E1E4DA;
$inverseOnSurface: #352E38; $inverseOnSurface: #2E322B;
$outline: #988D9D; $outline: #8D9387;
$outlineVariant: #4D4351; $outlineVariant: #43483F;
$shadow: #000000; $shadow: #000000;
$scrim: #000000; $scrim: #000000;
$surfaceTint: #E2B6FF; $surfaceTint: #A5D395;
$primary: #E2B6FF; $primary: #A5D395;
$onPrimary: #4D007B; $onPrimary: #11380B;
$primaryContainer: #6D00AC; $primaryContainer: #285020;
$onPrimaryContainer: #F3DAFF; $onPrimaryContainer: #C0EFB0;
$inversePrimary: #9000DF; $inversePrimary: #406836;
$secondary: #DEBCDF; $secondary: #BBCBB2;
$onSecondary: #402843; $onSecondary: #263422;
$secondaryContainer: #5A405D; $secondaryContainer: #3C4B37;
$onSecondaryContainer: #FDD9FD; $onSecondaryContainer: #D7E8CD;
$tertiary: #F0B5DE; $tertiary: #A0CFD2;
$onTertiary: #4B2142; $onTertiary: #003739;
$tertiaryContainer: #B680A7; $tertiaryContainer: #6B989B;
$onTertiaryContainer: #000000; $onTertiaryContainer: #000000;
$error: #FFB4AB; $error: #FFB4AB;
$onError: #690005; $onError: #690005;
$errorContainer: #93000A; $errorContainer: #93000A;
$onErrorContainer: #FFDAD6; $onErrorContainer: #FFDAD6;
$primaryFixed: #F3DAFF; $primaryFixed: #C0EFB0;
$primaryFixedDim: #E2B6FF; $primaryFixedDim: #A5D395;
$onPrimaryFixed: #2E004D; $onPrimaryFixed: #002200;
$onPrimaryFixedVariant: #6D00AC; $onPrimaryFixedVariant: #285020;
$secondaryFixed: #FCD7FB; $secondaryFixed: #D7E8CD;
$secondaryFixedDim: #DEBCDF; $secondaryFixedDim: #BBCBB2;
$onSecondaryFixed: #29132D; $onSecondaryFixed: #121F0E;
$onSecondaryFixedVariant: #583E5B; $onSecondaryFixedVariant: #3C4B37;
$tertiaryFixed: #FFD7F1; $tertiaryFixed: #BCEBEE;
$tertiaryFixedDim: #F0B5DE; $tertiaryFixedDim: #A0CFD2;
$onTertiaryFixed: #320C2C; $onTertiaryFixed: #002022;
$onTertiaryFixedVariant: #64385A; $onTertiaryFixedVariant: #1E4D50;
$success: #B5CCBA;
$onSuccess: #213528;
$successContainer: #374B3E;
$onSuccessContainer: #D1E9D6;
$term0: #161E14;
$term1: #9D5800;
$term2: #7E9D36;
$term3: #ABA930;
$term4: #4D8671;
$term5: #B96353;
$term6: #6C9D66;
$term7: #9E9C86;
$term8: #898674;
$term9: #CB7600;
$term10: #99C246;
$term11: #C9CE46;
$term12: #88A58F;
$term13: #D7886D;
$term14: #8DC07D;
$term15: #DDE5D5;