Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ It currently exports:
- Kitty `.conf`
- Alacritty `.toml`
- WezTerm `.lua`
- CotEditor `.cottheme`
- Source `.yaml` for terminal color scheme repos
- Portable JSON theme data for someone else or another tool to export later

Expand Down Expand Up @@ -93,7 +94,7 @@ At the save step, THEMaker asks what to export:
Supported format names are:

```text
iterm terminal kitty alacritty wezterm yaml data
iterm terminal kitty alacritty wezterm coteditor yaml data
```

| Format | Extension | Use |
Expand All @@ -103,6 +104,7 @@ iterm terminal kitty alacritty wezterm yaml data
| `kitty` | `.conf` | Include or copy into Kitty config. |
| `alacritty` | `.toml` | Import or copy into Alacritty config. |
| `wezterm` | `.lua` | Require or copy into WezTerm config. |
| `coteditor` | `.cottheme` | Import into CotEditor. |
| `yaml` | `.yaml` | Submit source schemes to terminal color scheme repos. |
| `data` | `.json` | Save portable theme data for another tool or maintainer. |

Expand Down Expand Up @@ -166,6 +168,11 @@ return {
Put the generated `.lua` file somewhere WezTerm can require it, or copy the
returned table into your WezTerm config.

CotEditor:

Open the generated `.cottheme` file, or import it from CotEditor's Appearance
settings.

YAML:

The `.yaml` export follows the source scheme guidance from
Expand Down Expand Up @@ -210,8 +217,7 @@ python3 -m unittest

## Releases

The first public release should be tagged as `v0.2.0` after the public export
branch is merged.
Public releases are tagged on GitHub. The first public release is `v0.2.0`.

## Credits

Expand Down
63 changes: 63 additions & 0 deletions examples/coolors-dark-balanced-sample.cottheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"attributes": {
"color": "#FFFFFF"
},
"background": {
"color": "#101418"
},
"characters": {
"color": "#FCEADE"
},
"commands": {
"color": "#FFFFFF"
},
"comments": {
"color": "#909190"
},
"highlight": {
"color": "#FCEADE",
"usesSystemSetting": false
},
"insertionPoint": {
"color": "#FFFFFF",
"usesSystemSetting": false
},
"invisibles": {
"color": "#616464"
},
"keywords": {
"color": "#25CED1"
},
"lineHighlight": {
"color": "#222A3680"
},
"metadata": {
"author": "@ylub",
"description": "Generated by THEMaker 0.3.0.",
"distributionURL": "https://github.com/ylub/themaker",
"license": "MIT"
},
"name": "coolors-dark-balanced-sample",
"numbers": {
"color": "#FF8A5B"
},
"selection": {
"color": "#334155",
"usesSystemSetting": false
},
"strings": {
"color": "#25CED1"
},
"text": {
"color": "#F8F8F2"
},
"types": {
"color": "#FCEADE"
},
"values": {
"color": "#FF8A5B"
},
"variables": {
"color": "#F8F8F2"
}
}
18 changes: 16 additions & 2 deletions test_themaker.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import plistlib
import tempfile
import unittest
Expand Down Expand Up @@ -86,15 +87,28 @@ def test_exporters_write_expected_files(self):
written, skipped = themaker.export_theme_files(
model,
out_dir,
["iterm", "terminal", "kitty", "alacritty", "wezterm", "yaml", "data"],
[
"iterm",
"terminal",
"kitty",
"alacritty",
"wezterm",
"coteditor",
"yaml",
"data",
],
)
self.assertEqual(skipped, [])
self.assertEqual(len(written), 7)
self.assertEqual(len(written), 8)
self.assertTrue((out_dir / "sample.itermcolors").exists())
self.assertTrue((out_dir / "sample.terminal").exists())
self.assertIn("foreground #F8F8F2", (out_dir / "sample.conf").read_text())
self.assertIn("[colors.primary]", (out_dir / "sample.toml").read_text())
self.assertIn("return {", (out_dir / "sample.lua").read_text())
cottheme = json.loads((out_dir / "sample.cottheme").read_text())
self.assertEqual(cottheme["background"]["color"], "#101418")
self.assertEqual(cottheme["text"]["color"], "#F8F8F2")
self.assertEqual(cottheme["metadata"]["author"], "@ylub")
yaml_text = (out_dir / "sample.yaml").read_text()
self.assertIn('color_01: "#101418"', yaml_text)
self.assertIn('color_16: "#FFFFFF"', yaml_text)
Expand Down
75 changes: 71 additions & 4 deletions themaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pathlib import Path

APP_NAME = "THEMaker"
APP_VERSION = "0.2.0"
APP_VERSION = "0.3.0"
APP_AUTHOR = "@ylub"
PROJECT_URL = "https://github.com/ylub/themaker"
COOLORS_URL = "https://coolors.co"
Expand All @@ -28,6 +28,7 @@
"kitty",
"alacritty",
"wezterm",
"coteditor",
"yaml",
"data",
)
Expand Down Expand Up @@ -296,6 +297,18 @@ def color_distance(first_hex, second_hex):
return sum((a - b) ** 2 for a, b in zip(first, second)) ** 0.5


def blend_colors(first_hex, second_hex, second_weight):
first = hex_to_rgb(first_hex)
second = hex_to_rgb(second_hex)
first_weight = 1 - second_weight
return rgb_to_hex(
tuple(
round(first_channel * first_weight + second_channel * second_weight)
for first_channel, second_channel in zip(first, second)
)
)


def complementary_color(hex_color, family, mode):
r, g, b = (channel / 255 for channel in hex_to_rgb(hex_color))
hue, lightness, saturation = colorsys.rgb_to_hls(r, g, b)
Expand Down Expand Up @@ -931,6 +944,56 @@ def write_gogh_yaml_theme(path, model):
path.write_text("\n".join(lines) + "\n", encoding="utf-8")


def cottheme_color(hex_color):
return {"color": format_hex(hex_color)}


def write_coteditor_theme(path, model):
colors = model["colors"]
normal = dict(zip(TERMINAL_ROLE_NAMES, colors["ansi"]))
muted = blend_colors(colors["background"], colors["foreground"], 0.55)
invisibles = blend_colors(colors["background"], colors["foreground"], 0.35)
line_highlight = blend_colors(colors["background"], colors["selection"], 0.5)
theme = {
"attributes": cottheme_color(normal["cyan"]),
"background": cottheme_color(colors["background"]),
"characters": cottheme_color(normal["yellow"]),
"commands": cottheme_color(normal["cyan"]),
"comments": cottheme_color(muted),
"highlight": {
"color": format_hex(normal["yellow"]),
"usesSystemSetting": False,
},
"insertionPoint": {
"color": format_hex(colors["cursor"]),
"usesSystemSetting": False,
},
"invisibles": cottheme_color(invisibles),
"keywords": cottheme_color(normal["blue"]),
"lineHighlight": {
"color": f"#{line_highlight}80",
},
"metadata": {
"author": APP_AUTHOR,
"description": f"Generated by {model['generator']}.",
"distributionURL": PROJECT_URL,
"license": "MIT",
},
"name": model["name"] or "THEMaker Theme",
"numbers": cottheme_color(normal["magenta"]),
"selection": {
"color": format_hex(colors["selection"]),
"usesSystemSetting": False,
},
"strings": cottheme_color(normal["green"]),
"text": cottheme_color(colors["foreground"]),
"types": cottheme_color(normal["yellow"]),
"values": cottheme_color(normal["magenta"]),
"variables": cottheme_color(colors["foreground"]),
}
path.write_text(json.dumps(theme, indent=2) + "\n", encoding="utf-8")


def lua_string(value):
return json.dumps(value)

Expand Down Expand Up @@ -968,6 +1031,7 @@ def write_theme_data(path, model):
"kitty": (".conf", write_kitty_theme),
"alacritty": (".toml", write_alacritty_theme),
"wezterm": (".lua", write_wezterm_theme),
"coteditor": (".cottheme", write_coteditor_theme),
"yaml": (".yaml", write_gogh_yaml_theme),
"data": (".json", write_theme_data),
}
Expand Down Expand Up @@ -1221,6 +1285,9 @@ def parse_export_formats(raw_value):
"alac": "alacritty",
"w": "wezterm",
"wez": "wezterm",
"cot": "coteditor",
"cotedit": "coteditor",
"cottheme": "coteditor",
"y": "yaml",
"gogh": "yaml",
"iterm-yaml": "yaml",
Expand All @@ -1244,12 +1311,12 @@ def parse_export_formats(raw_value):
def choose_export_formats():
print("\nExport options:")
print(
" all iTerm2, macOS Terminal, Kitty, Alacritty, WezTerm, YAML, and data JSON"
" all iTerm2, macOS Terminal, Kitty, Alacritty, WezTerm, CotEditor, YAML, and data JSON"
)
print(" one Choose one format")
print(" some Choose a few formats")
print(" data Save portable JSON data for someone else to export")
print("Formats: iterm, terminal, kitty, alacritty, wezterm, yaml, data")
print("Formats: iterm, terminal, kitty, alacritty, wezterm, coteditor, yaml, data")
while True:
raw = ask("Export formats", "all")
if raw.strip().lower() == "one":
Expand Down Expand Up @@ -1584,7 +1651,7 @@ def build_parser():
parser.add_argument(
"--format",
dest="formats",
help="Export formats: all, data, or any of iterm, terminal, kitty, alacritty, wezterm, yaml, data.",
help="Export formats: all, data, or any of iterm, terminal, kitty, alacritty, wezterm, coteditor, yaml, data.",
)
parser.add_argument(
"--list-themes",
Expand Down
Loading