From b8abe92f91be8099d64b1da41e63611321362f7f Mon Sep 17 00:00:00 2001 From: Daylin Morgan Date: Wed, 26 Apr 2023 20:03:36 -0500 Subject: [PATCH] feat: implement catppuccin in nim --- .gitignore | 6 + README.md | 43 ++--- catppuccin.nimble | 35 ++++ examples/config.nims | 2 + examples/term.nim | 28 ++++ examples/use_chroma.nim | 11 ++ src/catppuccin.nim | 18 ++ src/catppuccin/chroma.nim | 325 +++++++++++++++++++++++++++++++++++++ src/catppuccin/palette.nim | 112 +++++++++++++ tests/config.nims | 1 + tests/test1.nim | 9 + tools/generate.nim | 46 ++++++ 12 files changed, 609 insertions(+), 27 deletions(-) create mode 100644 .gitignore create mode 100644 catppuccin.nimble create mode 100644 examples/config.nims create mode 100644 examples/term.nim create mode 100644 examples/use_chroma.nim create mode 100644 src/catppuccin.nim create mode 100644 src/catppuccin/chroma.nim create mode 100644 src/catppuccin/palette.nim create mode 100644 tests/config.nims create mode 100644 tests/test1.nim create mode 100644 tools/generate.nim diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c07d89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/public/ +/tools/palette-porcelain.json + +/tests/** +!/tests/*.nim +!/tests/*.nims diff --git a/README.md b/README.md index 75a5d0a..21e5b93 100644 --- a/README.md +++ b/README.md @@ -11,41 +11,30 @@

-

- -

- -## Previews - -
-๐ŸŒป Latte - -
-
-๐Ÿชด Frappรฉ - -
-
-๐ŸŒบ Macchiato - -
-
-๐ŸŒฟ Mocha - -
## Usage -1. Clone this repository locally -2. Open the app's settings -3. Select `import theme` and browse to where you cloned Catppuccin -4. Select it +```sh +nimble install https://github.com/catppuccin/nim +``` + +The `catppuccin` nim library was designed to interface with [`treeform/chroma`](https://github.com/treeform/chroma), however it is not required for basic usage. Some of the basic color types and transformations have been ported from `chroma` + +If you do wish to access `catppuccin` colors alongside `chroma` compile with `-d:inheritChroma` (see `./examples/use_chroma.nim`) to use the color types defined by `chroma`. + +### Example + +```nim +import catppuccin + +echo mocha.rosewater.color().toHex() +``` ## ๐Ÿ™‹ FAQ - Q: **_"Where can I find the doc?"_**\ - A: Run `nim mkDocs` + A: Run `nimble docs` ## ๐Ÿ’ Thanks to diff --git a/catppuccin.nimble b/catppuccin.nimble new file mode 100644 index 0000000..e9e7899 --- /dev/null +++ b/catppuccin.nimble @@ -0,0 +1,35 @@ +import std/[os,strformat] +# Package + +version = "0.1.0" +author = "Daylin Morgan" +description = "Soothing pastel theme for nim" +license = "MIT" +srcDir = "src" + + +# Dependencies + +requires "nim >= 1.6.12" + +task gen, "generate src/catppuccin/palette.nim": + let paletteJson = "./tools/palette-porcelain.json" + let srcUrl = "https://raw.githubusercontent.com/catppuccin/palette/main/palette-porcelain.json" + if not fileExists(paletteJson): exec &"wget -O {paletteJson} {srcUrl}" + exec "nim r ./tools/generate.nim" + + +task docs, "Deploy doc html + search index to public/ directory": + let + deployDir = getCurrentDir() / "public" + pkgName = "catppuccin" + srcFile = getCurrentDir() / "src" / (pkgName & ".nim") + gitUrl = "https://github.com/daylinmorgan/catppuccin-nim" + selfExec &"doc --index:on --git.url:{gitUrl} --outdir:{deployDir} --project {srcFile}" + withDir deployDir: + mvFile(pkgName & ".html", "index.html") + for file in walkDirRec(".", {pcFile}): + # As we renamed the file, we need to rename that in hyperlinks + exec(r"sed -i -r 's|$1\.html|index.html|g' $2" % [pkgName, file]) + # drop 'src/' from titles + exec(r"sed -i -r 's/<(.*)>src\//<\1>/' $1" % file) diff --git a/examples/config.nims b/examples/config.nims new file mode 100644 index 0000000..07d71fa --- /dev/null +++ b/examples/config.nims @@ -0,0 +1,2 @@ +switch("path", "$projectDir/../src") +switch("hints", "off") diff --git a/examples/term.nim b/examples/term.nim new file mode 100644 index 0000000..76af9c7 --- /dev/null +++ b/examples/term.nim @@ -0,0 +1,28 @@ +import std/[strformat] +import catppuccin + +const ansiReset = "\e[0m" + +proc ansi(s: string, c: ColorRGB): string = + let code = &"\e[48;2;{c.r};{c.g};{c.b}m" + result.add(code) + result.add(s) + result.add(ansiReset) + + +when isMainModule: + let flavors = @[ + ("latte", latte), + ("frappe", frappe), + ("macchiato", macchiato), + ("mocha", mocha) + ] + + for (name, flavor) in flavors: + + echo name + + for name, color in flavor.fieldPairs(): + write(stdout, " ".ansi(color)) + + write(stdout, "\n\n") diff --git a/examples/use_chroma.nim b/examples/use_chroma.nim new file mode 100644 index 0000000..4c56897 --- /dev/null +++ b/examples/use_chroma.nim @@ -0,0 +1,11 @@ +{.define: inheritChroma.} + +import std/[strutils] + +import catppuccin + + +when isMainModule: + echo "Mocha colors as CMYK" + for n, c in mocha.fieldPairs(): + echo alignLeft(n, 9) & " -> " & $c.color().asCmyk() diff --git a/src/catppuccin.nim b/src/catppuccin.nim new file mode 100644 index 0000000..03e3924 --- /dev/null +++ b/src/catppuccin.nim @@ -0,0 +1,18 @@ +when not defined(inheritChroma): + import catppuccin/chroma +else: + import chroma + +type + Flavor = object + rosewater*, flamingo*, pink*, mauve*, red*, maroon*, peach*, yellow*, + green*, teal*, sky*, sapphire*, blue*, lavender*, text*, subtext1*, + subtext0*, overlay2*, + overlay1*, surface2*, surface1*, surface0*, base*, mantle*, + crust*: ColorRGB + +include catppuccin/palette + + +export mocha, latte, macchiato, frappe +export chroma diff --git a/src/catppuccin/chroma.nim b/src/catppuccin/chroma.nim new file mode 100644 index 0000000..397c7f7 --- /dev/null +++ b/src/catppuccin/chroma.nim @@ -0,0 +1,325 @@ +import std/[hashes, math, strutils] +## standalone types/methods ported from treeform/chroma +## + +# chroma/colortypes --------------- +type + Color* = object + ## Main color type, float32 points + r*: float32 ## red (0-1) + g*: float32 ## green (0-1) + b*: float32 ## blue (0-1) + a*: float32 ## alpha (0-1, 0 is fully transparent) + + # Color Space: rgb + ColorRGB* = object + ## Color stored as 3 uint8s + r*: uint8 ## Red 0-255 + g*: uint8 ## Green 0-255 + b*: uint8 ## Blue 0-255 + + # Color Space: rgba + ColorRGBA* = object + ## Color stored as 4 uint8s + r*: uint8 ## Red 0-255 + g*: uint8 ## Green 0-255 + b*: uint8 ## Blue 0-255 + a*: uint8 ## Alpha 0-255 + + ColorRGBX* = object + ## Premultiplied alpha RGBA color stored as 4 uint8s + r*: uint8 ## Red 0-a + g*: uint8 ## Green 0-a + b*: uint8 ## Blue 0-a + a*: uint8 ## Alpha 0-255 + + # Color Space: HSL + ColorHSL* = object + ## HSL attempts to resemble more perceptual color models + h*: float32 ## hue 0 to 360 + s*: float32 ## saturation 0 to 100 + l*: float32 ## lightness 0 to 100 + + SomeColor* = Color|ColorRGB|ColorRGBA|ColorHSL + + InvalidColor* = object of ValueError + +proc color*(r, g, b: float32, a: float32 = 1.0): Color {.inline.} = + ## Creates from floats like: + ## * color(1,0,0) -> red + ## * color(0,1,0) -> green + ## * color(0,0,1) -> blue + ## * color(0,0,0,1) -> opaque black + ## * color(0,0,0,0) -> transparent black + Color(r: r, g: g, b: b, a: a) + +proc rgb*(r, g, b: uint8): ColorRGB {.inline.} = + ## Creates from uint8s like: + ## * rgba(255,0,0) -> red + ## * rgba(0,255,0) -> green + ## * rgba(0,0,255) -> blue + ColorRGB(r: r, g: g, b: b) + +proc rgba*(r, g, b, a: uint8): ColorRGBA {.inline.} = + ## Creates from uint8s like: + ## * rgba(255,0,0,255) -> red + ## * rgba(0,255,0,255) -> green + ## * rgba(0,0,255,255) -> blue + ## * rgba(0,0,0,255) -> opaque black + ## * rgba(0,0,0,0) -> transparent black + ColorRGBA(r: r, g: g, b: b, a: a) + +proc hsl*(h, s, l: float32): ColorHSL {.inline.} = + ColorHSL(h: h, s: s, l: l) + +# chroma/colortypes --------------- + +# chroma/transformations ---------- + +proc rgba*(c: ColorRGBX): ColorRGBA {.inline.} = + ## Convert a premultiplied alpha RGBA to a straight alpha RGBA. + result.r = c.r + result.g = c.g + result.b = c.b + result.a = c.a + if result.a != 0 and result.a != 255: + let multiplier = round((255 / c.a.float32) * 255).uint32 + result.r = ((result.r.uint32 * multiplier + 127) div 255).uint8 + result.g = ((result.g.uint32 * multiplier + 127) div 255).uint8 + result.b = ((result.b.uint32 * multiplier + 127) div 255).uint8 + +proc rgb*(c: Color): ColorRGB {.inline.} = + ## Convert Color to ColorRGB + result.r = round(c.r * 255).uint8 + result.g = round(c.g * 255).uint8 + result.b = round(c.b * 255).uint8 + +proc color*(c: ColorRGB): Color {.inline.} = + ## Convert ColorRGB to Color + result.r = float32(c.r) / 255 + result.g = float32(c.g) / 255 + result.b = float32(c.b) / 255 + result.a = 1.0 + +proc rgba*(c: Color): ColorRGBA {.inline.} = + ## Convert Color to ColorRGBA + result.r = round(c.r * 255).uint8 + result.g = round(c.g * 255).uint8 + result.b = round(c.b * 255).uint8 + result.a = round(c.a * 255).uint8 + +proc color*(c: ColorRGBA): Color {.inline.} = + ## Convert ColorRGBA to Color + result.r = float32(c.r) / 255 + result.g = float32(c.g) / 255 + result.b = float32(c.b) / 255 + result.a = float32(c.a) / 255 + +proc min3(a, b, c: float32): float32 {.inline.} = min(a, min(b, c)) +proc max3(a, b, c: float32): float32 {.inline.} = max(a, max(b, c)) + +proc hsl*(c: Color): ColorHSL = + ## convert Color to ColorHSL + let + min = min3(c.r, c.g, c.b) + max = max3(c.r, c.g, c.b) + delta = max - min + if max == min: + result.h = 0.0 + elif c.r == max: + result.h = (c.g - c.b) / delta + elif c.g == max: + result.h = 2 + (c.b - c.r) / delta + elif c.b == max: + result.h = 4 + (c.r - c.g) / delta + + result.h = min(result.h * 60, 360) + if result.h < 0: + result.h += 360 + + result.l = (min + max) / 2 + + if max == min: + result.s = 0 + elif result.l <= 0.5: + result.s = delta / (max + min) + else: + result.s = delta / (2 - max - min) + + result.s *= 100 + result.l *= 100 + +func fixupColor[T: int | float32](r, g, b: var T): bool = + ## performs a fixup of the given r, g, b values and returnes whether + ## any of the values was modified. + ## This func works on integers or floats. It is only used within the + ## conversion of `Color -> ColorHCL` (on integers) and `ColorHCL -> Color` + ## (on floats). + template fixC(c: untyped): untyped = + if c < T(0): + c = T(0) + result = true + when T is int: + if c > 255: + c = 255 + result = true + else: + if c > 1.0: + c = 1.0 + result = true + fixC(r) + fixC(g) + fixC(b) + +# overload working on `var Color`. It's `discardable`, because in our usage +# here we do not really care whether a value was modified. +func fixupColor(c: var Color): bool {.inline, discardable.} = + fixupColor(c.r, c.g, c.b) + +proc color*(c: ColorHSL): Color = + ## convert ColorHSL to Color + let + h = c.h / 360 + s = c.s / 100 + l = c.l / 100 + var t1, t2, t3: float32 + if s == 0.0: + return color(l, l, l) + if l < 0.5: + t2 = l * (1 + s) + else: + t2 = l + s - l * s + t1 = 2 * l - t2 + + var rgb: array[3, float32] + for i in 0..2: + t3 = h + 1.0 / 3.0 * - (float32(i) - 1.0) + if t3 < 0: + t3 += 1 + elif t3 > 1: + t3 -= 1 + + var val: float32 + if 6 * t3 < 1: + val = t1 + (t2 - t1) * 6 * t3 + elif 2 * t3 < 1: + val = t2 + elif 3 * t3 < 2: + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6 + else: + val = t1 + + rgb[i] = val + result.r = rgb[0] + result.g = rgb[1] + result.b = rgb[2] + result.a = 1.0 + fixupColor(result) + result + +proc color*(c: Color): Color {.inline.} = + c + +proc to*[T: SomeColor](c: SomeColor, toColor: typedesc[T]): T {.inline.} = + ## Allows conversion of transformation of a color in any color space into any + ## other color space. + when type(c) is T: + c + else: + when toColor is Color: + c.color + elif toColor is ColorRGB: + c.color.rgb + elif toColor is ColorRGBA: + c.color.rgba + elif toColor is ColorHSL: + c.color.hsl + +proc asColor*(c: SomeColor): Color {.inline.} = c.to(Color) +proc asRgb*(c: SomeColor): ColorRGB {.inline.} = c.to(ColorRGB) +proc asHsl*(c: SomeColor): ColorHSL {.inline.} = c.to(ColorHSL) + +# chroma/transformations ---------- + +# chroma -------------------------- + +proc toHex(a: float32): string {.inline.} = toHex(int(a)) + +proc `$`*(c: Color): string = + ## Returns colors as "(r, g, b, a)". + "(" & $c.r & ", " & $c.g & ", " & $c.b & ", " & $c.a & ")" + +func hash*(c: Color): Hash = + ## Hashes a Color - used in tables. + hash((c.r, c.g, c.b, c.a)) + +func hash*(c: ColorRGB): Hash = + ## Hashes a ColorRGB - used in tables. + hash((c.r, c.g, c.b)) + +func hash*(c: ColorRGBA): Hash = + ## Hashes a ColorRGB - used in tables. + hash((c.r, c.g, c.b, c.a)) + +func hash*(c: ColorHSL): Hash = + ## Hashes a ColorHSL - used in tables. + hash((c.h, c.s, c.l)) + +proc toHex*(c: Color): string = + ## Formats color as hex (upper case): + ## * red -> FF0000 + ## * blue -> 0000FF + ## * white -> FFFFFF + template pair(n: float32): string = + toHex(n*255)[^2..^1] + pair(c.r) & pair(c.g) & pair(c.b) + +proc toHexAlpha*(c: Color): string = + ## Formats color as hex (upper case): + ## * red -> FF0000FF + ## * blue -> 0000FFFF + ## * white -> FFFFFFFF + ## * opaque black -> 000000FF + ## * transparent black -> 00000000 + template pair(n: float32): string = + toHex(n*255)[^2..^1] + pair(c.r) & pair(c.g) & pair(c.b) & pair(c.a) + +proc toHtmlHex*(c: Color): string = + ## Formats color as HTML hex (upper case): + ## * red -> #FF0000 + ## * blue -> #0000FF + ## * white -> #FFFFFF + '#' & c.toHex() + +proc toHtmlHexTiny*(c: Color): string = + ## Formats color as HTML 3 hex numbers (upper case): + ## * red -> #F00 + ## * blue -> #00F + ## * white -> #FFF + proc pair(n: float32): string = + toHex(n*15)[^1..^1] + return '#' & pair(c.r) & pair(c.g) & pair(c.b) + +proc toHtmlRgb*(c: Color): string = + ## Parses colors in html's rgb format: + ## * red -> rgb(255, 0, 0) + ## * blue -> rgb(0,0,255) + ## * white -> rgb(255,255,255) + "rgb(" & + $round(c.r * 255).int & ", " & + $round(c.g * 255).int & ", " & + $round(c.b * 255).int & + ")" + +proc toHtmlRgba*(c: Color): string = + ## Parses colors in html's rgb format: + ## * red -> rgb(255, 0, 0) + ## * blue -> rgb(0,0,255) + ## * white -> rgb(255,255,255) + "rgba(" & + $round(c.r * 255).int & ", " & + $round(c.g * 255).int & ", " & + $round(c.b * 255).int & ", " & + $c.a & + ")" diff --git a/src/catppuccin/palette.nim b/src/catppuccin/palette.nim new file mode 100644 index 0000000..c06d9b9 --- /dev/null +++ b/src/catppuccin/palette.nim @@ -0,0 +1,112 @@ +# DO NOT EDIT this file is autogenerated by tools/generate.nim! + +const + latte* = Flavor( + rosewater: ColorRGB(r: 220, g: 138, b: 120), + flamingo: ColorRGB(r: 221, g: 120, b: 120), + pink: ColorRGB(r: 234, g: 118, b: 203), + mauve: ColorRGB(r: 136, g: 57, b: 239), + red: ColorRGB(r: 210, g: 15, b: 57), + maroon: ColorRGB(r: 230, g: 69, b: 83), + peach: ColorRGB(r: 254, g: 100, b: 11), + yellow: ColorRGB(r: 223, g: 142, b: 29), + green: ColorRGB(r: 64, g: 160, b: 43), + teal: ColorRGB(r: 23, g: 146, b: 153), + sky: ColorRGB(r: 4, g: 165, b: 229), + sapphire: ColorRGB(r: 32, g: 159, b: 181), + blue: ColorRGB(r: 30, g: 102, b: 245), + lavender: ColorRGB(r: 114, g: 135, b: 253), + text: ColorRGB(r: 76, g: 79, b: 105), + subtext1: ColorRGB(r: 92, g: 95, b: 119), + subtext0: ColorRGB(r: 108, g: 111, b: 133), + overlay2: ColorRGB(r: 124, g: 127, b: 147), + overlay1: ColorRGB(r: 140, g: 143, b: 161), + surface2: ColorRGB(r: 172, g: 176, b: 190), + surface1: ColorRGB(r: 188, g: 192, b: 204), + surface0: ColorRGB(r: 204, g: 208, b: 218), + base: ColorRGB(r: 239, g: 241, b: 245), + mantle: ColorRGB(r: 230, g: 233, b: 239), + crust: ColorRGB(r: 220, g: 224, b: 232) + ) + frappe* = Flavor( + rosewater: ColorRGB(r: 242, g: 213, b: 207), + flamingo: ColorRGB(r: 238, g: 190, b: 190), + pink: ColorRGB(r: 244, g: 184, b: 228), + mauve: ColorRGB(r: 202, g: 158, b: 230), + red: ColorRGB(r: 231, g: 130, b: 132), + maroon: ColorRGB(r: 234, g: 153, b: 156), + peach: ColorRGB(r: 239, g: 159, b: 118), + yellow: ColorRGB(r: 229, g: 200, b: 144), + green: ColorRGB(r: 166, g: 209, b: 137), + teal: ColorRGB(r: 129, g: 200, b: 190), + sky: ColorRGB(r: 153, g: 209, b: 219), + sapphire: ColorRGB(r: 133, g: 193, b: 220), + blue: ColorRGB(r: 140, g: 170, b: 238), + lavender: ColorRGB(r: 186, g: 187, b: 241), + text: ColorRGB(r: 198, g: 208, b: 245), + subtext1: ColorRGB(r: 181, g: 191, b: 226), + subtext0: ColorRGB(r: 165, g: 173, b: 206), + overlay2: ColorRGB(r: 148, g: 156, b: 187), + overlay1: ColorRGB(r: 131, g: 139, b: 167), + surface2: ColorRGB(r: 98, g: 104, b: 128), + surface1: ColorRGB(r: 81, g: 87, b: 109), + surface0: ColorRGB(r: 65, g: 69, b: 89), + base: ColorRGB(r: 48, g: 52, b: 70), + mantle: ColorRGB(r: 41, g: 44, b: 60), + crust: ColorRGB(r: 35, g: 38, b: 52) + ) + macchiato* = Flavor( + rosewater: ColorRGB(r: 244, g: 219, b: 214), + flamingo: ColorRGB(r: 240, g: 198, b: 198), + pink: ColorRGB(r: 245, g: 189, b: 230), + mauve: ColorRGB(r: 198, g: 160, b: 246), + red: ColorRGB(r: 237, g: 135, b: 150), + maroon: ColorRGB(r: 238, g: 153, b: 160), + peach: ColorRGB(r: 245, g: 169, b: 127), + yellow: ColorRGB(r: 238, g: 212, b: 159), + green: ColorRGB(r: 166, g: 218, b: 149), + teal: ColorRGB(r: 139, g: 213, b: 202), + sky: ColorRGB(r: 145, g: 215, b: 227), + sapphire: ColorRGB(r: 125, g: 196, b: 228), + blue: ColorRGB(r: 138, g: 173, b: 244), + lavender: ColorRGB(r: 183, g: 189, b: 248), + text: ColorRGB(r: 202, g: 211, b: 245), + subtext1: ColorRGB(r: 184, g: 192, b: 224), + subtext0: ColorRGB(r: 165, g: 173, b: 203), + overlay2: ColorRGB(r: 147, g: 154, b: 183), + overlay1: ColorRGB(r: 128, g: 135, b: 162), + surface2: ColorRGB(r: 91, g: 96, b: 120), + surface1: ColorRGB(r: 73, g: 77, b: 100), + surface0: ColorRGB(r: 54, g: 58, b: 79), + base: ColorRGB(r: 36, g: 39, b: 58), + mantle: ColorRGB(r: 30, g: 32, b: 48), + crust: ColorRGB(r: 24, g: 25, b: 38) + ) + mocha* = Flavor( + rosewater: ColorRGB(r: 245, g: 224, b: 220), + flamingo: ColorRGB(r: 242, g: 205, b: 205), + pink: ColorRGB(r: 245, g: 194, b: 231), + mauve: ColorRGB(r: 203, g: 166, b: 247), + red: ColorRGB(r: 243, g: 139, b: 168), + maroon: ColorRGB(r: 235, g: 160, b: 172), + peach: ColorRGB(r: 250, g: 179, b: 135), + yellow: ColorRGB(r: 249, g: 226, b: 175), + green: ColorRGB(r: 166, g: 227, b: 161), + teal: ColorRGB(r: 148, g: 226, b: 213), + sky: ColorRGB(r: 137, g: 220, b: 235), + sapphire: ColorRGB(r: 116, g: 199, b: 236), + blue: ColorRGB(r: 137, g: 180, b: 250), + lavender: ColorRGB(r: 180, g: 190, b: 254), + text: ColorRGB(r: 205, g: 214, b: 244), + subtext1: ColorRGB(r: 186, g: 194, b: 222), + subtext0: ColorRGB(r: 166, g: 173, b: 200), + overlay2: ColorRGB(r: 147, g: 153, b: 178), + overlay1: ColorRGB(r: 127, g: 132, b: 156), + surface2: ColorRGB(r: 88, g: 91, b: 112), + surface1: ColorRGB(r: 69, g: 71, b: 90), + surface0: ColorRGB(r: 49, g: 50, b: 68), + base: ColorRGB(r: 30, g: 30, b: 46), + mantle: ColorRGB(r: 24, g: 24, b: 37), + crust: ColorRGB(r: 17, g: 17, b: 27) + ) + diff --git a/tests/config.nims b/tests/config.nims new file mode 100644 index 0000000..3bb69f8 --- /dev/null +++ b/tests/config.nims @@ -0,0 +1 @@ +switch("path", "$projectDir/../src") \ No newline at end of file diff --git a/tests/test1.nim b/tests/test1.nim new file mode 100644 index 0000000..68c7ef7 --- /dev/null +++ b/tests/test1.nim @@ -0,0 +1,9 @@ +import unittest + +import catppuccin + +test "color": + check mocha.rosewater == ColorRGB(r: 245, g: 224, b: 220) + +test "convert": + check mocha.rosewater.color().toHex() == "F5E0DC" diff --git a/tools/generate.nim b/tools/generate.nim new file mode 100644 index 0000000..269f41c --- /dev/null +++ b/tools/generate.nim @@ -0,0 +1,46 @@ +import std/[json, strformat, strutils] + + +type + Color = object + hex: string + rgb: array[3, int] + hsl: array[3, float] + Flavor = object + rosewater, flamingo, pink, mauve, red, maroon, peach, yellow, green, teal, + sky, sapphire, blue, lavender, text, subtext1, subtext0, overlay2, + overlay1, surface2, surface1, surface0, base, mantle, crust: Color + Palette = object + latte, frappe, macchiato, mocha: Flavor + +proc createColor(c: Color, name: string): string = + result = &""" + {name}: ColorRGB(r: {c.rgb[0]}, g: {c.rgb[1]}, b: {c.rgb[2]})""" + +proc createFlavor(f: Flavor, name: string): string = + var colorsDef: seq[string] + for name, color in f.fieldPairs(): + colorsDef.add color.createColor(name) + + result = &""" + {name}* = Flavor( +{colorsDef.join(",\n")} + )""" + + +when isMainModule: + const paletteJsonStr = slurp "./palette-porcelain.json" + const doNotEdit = "# DO NOT EDIT this file is autogenerated by tools/generate.nim!" + + let palette = paletteJsonStr.parseJson().to(Palette) + var flavors: string + for name, flavor in palette.fieldPairs(): + flavors &= flavor.createFlavor(name) & "\n" + + let catppuccin = &""" +{doNotEdit} + +const +{flavors} +""" + writeFile("./src/catppuccin/palette.nim", catppuccin)