407 lines
15 KiB
Python
407 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from textwrap import dedent
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
PALETTE_FILE = ROOT / "palette.json"
|
|
|
|
|
|
def load_palette() -> dict[str, str]:
|
|
data = json.loads(PALETTE_FILE.read_text())
|
|
return data["colors"]
|
|
|
|
|
|
def hex_no_hash(value: str) -> str:
|
|
return value.lstrip("#")
|
|
|
|
|
|
def hex_to_rgb(value: str) -> tuple[int, int, int]:
|
|
value = hex_no_hash(value)
|
|
return tuple(int(value[index:index + 2], 16) for index in (0, 2, 4))
|
|
|
|
|
|
def camel_from_snake(name: str) -> str:
|
|
head, *tail = name.split("_")
|
|
return head + "".join(part.capitalize() for part in tail)
|
|
|
|
|
|
def write(path: Path, content: str) -> None:
|
|
path.write_text(content.rstrip() + "\n")
|
|
|
|
|
|
def sgr_fg(value: str, *, bold: bool = False) -> str:
|
|
red, green, blue = hex_to_rgb(value)
|
|
parts = []
|
|
if bold:
|
|
parts.append("1")
|
|
parts.append(f"38;2;{red};{green};{blue}")
|
|
return ";".join(parts)
|
|
|
|
|
|
def build_ls_colors(colors: dict[str, str]) -> str:
|
|
entries = {
|
|
"no": "0",
|
|
"fi": "0",
|
|
"di": sgr_fg(colors["keyword"], bold=True),
|
|
"ln": sgr_fg(colors["field"], bold=True),
|
|
"or": sgr_fg(colors["error"]),
|
|
"mi": "0",
|
|
"so": sgr_fg(colors["field"]),
|
|
"pi": sgr_fg(colors["string"]),
|
|
"do": sgr_fg(colors["func"], bold=True),
|
|
"bd": sgr_fg(colors["number"]),
|
|
"cd": sgr_fg(colors["number"]),
|
|
"su": sgr_fg(colors["error"], bold=True),
|
|
"sg": sgr_fg(colors["escape"], bold=True),
|
|
"ex": sgr_fg(colors["func"], bold=True),
|
|
"*.tar": sgr_fg(colors["type_alt"]),
|
|
"*.tgz": sgr_fg(colors["type_alt"]),
|
|
"*.gz": sgr_fg(colors["type_alt"]),
|
|
"*.bz2": sgr_fg(colors["type_alt"]),
|
|
"*.xz": sgr_fg(colors["type_alt"]),
|
|
"*.zip": sgr_fg(colors["type_alt"]),
|
|
"*.7z": sgr_fg(colors["type_alt"]),
|
|
"*.zst": sgr_fg(colors["type_alt"]),
|
|
"*.rar": sgr_fg(colors["type_alt"]),
|
|
"*.jpg": sgr_fg(colors["type"]),
|
|
"*.jpeg": sgr_fg(colors["type"]),
|
|
"*.png": sgr_fg(colors["type"]),
|
|
"*.gif": sgr_fg(colors["type"]),
|
|
"*.svg": sgr_fg(colors["type"]),
|
|
"*.webp": sgr_fg(colors["type"]),
|
|
"*.mp3": sgr_fg(colors["type"]),
|
|
"*.flac": sgr_fg(colors["type"]),
|
|
"*.wav": sgr_fg(colors["type"]),
|
|
"*.mp4": sgr_fg(colors["type"]),
|
|
"*.mkv": sgr_fg(colors["type"]),
|
|
"*.mov": sgr_fg(colors["type"]),
|
|
"*.pdf": sgr_fg(colors["comment"]),
|
|
"*.md": sgr_fg(colors["comment"]),
|
|
"*.txt": sgr_fg(colors["comment"]),
|
|
"*.log": sgr_fg(colors["comment"]),
|
|
"*.conf": sgr_fg(colors["string"]),
|
|
"*.json": sgr_fg(colors["string"]),
|
|
"*.yaml": sgr_fg(colors["string"]),
|
|
"*.yml": sgr_fg(colors["string"]),
|
|
"*.toml": sgr_fg(colors["string"]),
|
|
"*.ini": sgr_fg(colors["string"]),
|
|
"*.sh": sgr_fg(colors["func"]),
|
|
"*.bash": sgr_fg(colors["func"]),
|
|
"*.zsh": sgr_fg(colors["func"]),
|
|
"*.py": sgr_fg(colors["field"]),
|
|
"*.js": sgr_fg(colors["field"]),
|
|
"*.ts": sgr_fg(colors["field"]),
|
|
"*.tsx": sgr_fg(colors["field"]),
|
|
"*.jsx": sgr_fg(colors["field"]),
|
|
"*.lua": sgr_fg(colors["field"]),
|
|
"*.rs": sgr_fg(colors["field"]),
|
|
"*.go": sgr_fg(colors["field"]),
|
|
"*.c": sgr_fg(colors["field"]),
|
|
"*.h": sgr_fg(colors["field"]),
|
|
"*.cpp": sgr_fg(colors["field"]),
|
|
"*.hpp": sgr_fg(colors["field"]),
|
|
"*.cs": sgr_fg(colors["field"]),
|
|
}
|
|
return ":".join(f"{key}={value}" for key, value in entries.items())
|
|
|
|
|
|
def render_palette_sh(colors: dict[str, str]) -> str:
|
|
lines = [
|
|
"# Generated from palette.json by generate.py. Do not edit directly.",
|
|
"",
|
|
]
|
|
for name, value in colors.items():
|
|
lines.append(f'export RIDER_{name.upper()}="{value}"')
|
|
lines.append("")
|
|
lines.append("# Shared Rider palette for GNU ls and compatible tools.")
|
|
lines.append(f'export LS_COLORS="{build_ls_colors(colors)}"')
|
|
return "\n".join(lines)
|
|
|
|
|
|
def render_palette_css(colors: dict[str, str]) -> str:
|
|
lines = [
|
|
"/* Generated from palette.json by generate.py. Do not edit directly. */",
|
|
]
|
|
for name, value in colors.items():
|
|
lines.append(f"@define-color rider-{name.replace('_', '-')} {value};")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def render_palette_hypr(colors: dict[str, str]) -> str:
|
|
lines = [
|
|
"# Generated from palette.json by generate.py. Do not edit directly.",
|
|
"# Usage:",
|
|
"# source = ~/.config/rider-palette/palette.hyprland.conf",
|
|
"",
|
|
]
|
|
for name, value in colors.items():
|
|
lines.append(f"${name} = rgb({hex_no_hash(value)})")
|
|
lines.append(f"${camel_from_snake(name)}Alpha = {hex_no_hash(value)}")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def render_palette_rasi(colors: dict[str, str]) -> str:
|
|
lines = [
|
|
"/* Generated from palette.json by generate.py. Do not edit directly. */",
|
|
"* {",
|
|
]
|
|
for name, value in colors.items():
|
|
lines.append(f" rider-{name.replace('_', '-')}: {value};")
|
|
lines.append("}")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def render_tmux_conf(colors: dict[str, str]) -> str:
|
|
return dedent(
|
|
f"""
|
|
# Generated from palette.json by generate.py. Do not edit directly.
|
|
# Source this near the end of ~/.tmux.conf.
|
|
|
|
set -g status-style "bg={colors['bg']},fg={colors['fg_bright']}"
|
|
set -g status-left-style "bg={colors['bg']},fg={colors['fg_bright']}"
|
|
set -g status-right-style "bg={colors['bg']},fg={colors['fg_bright']}"
|
|
set -g status-left-length 48
|
|
set -g status-right-length 80
|
|
set -g status-justify centre
|
|
set -g window-status-separator " "
|
|
set -g status-left "#[fg={colors['bg']},bg={colors['func']},bold] #S #[fg={colors['func']},bg={colors['bg']}]"
|
|
set -g status-right "#[fg={colors['field']},bg={colors['bg']}]#[fg={colors['bg']},bg={colors['field']}] %Y-%m-%d #[fg={colors['string']},bg={colors['field']}]#[fg={colors['bg']},bg={colors['string']}] %H:%M#[fg={colors['string']},bg={colors['bg']}]"
|
|
|
|
set -g message-style "bg={colors['cursor_line']},fg={colors['fg_bright']}"
|
|
set -g message-command-style "bg={colors['cursor_line']},fg={colors['fg_bright']}"
|
|
set -g mode-style "bg={colors['selection']},fg={colors['fg_bright']}"
|
|
|
|
set -g pane-border-style "fg={colors['border']}"
|
|
set -g pane-active-border-style "fg={colors['keyword']}"
|
|
set -g clock-mode-colour "{colors['keyword']}"
|
|
|
|
set -g window-status-style "bg={colors['bg']},fg={colors['fg_gutter']}"
|
|
set -g window-status-current-style "bg={colors['bg']},fg={colors['fg_bright']}"
|
|
set -g window-status-current-format "#[fg={colors['func']},bg={colors['bg']}]#[fg={colors['bg']},bg={colors['func']},bold] #I #[fg={colors['func']},bg={colors['border']}]#[fg={colors['fg_bright']},bg={colors['border']}] #W #[fg={colors['border']},bg={colors['bg']}]"
|
|
set -g window-status-format "#[fg={colors['cursor_line']},bg={colors['bg']}]#[fg={colors['fg_gutter']},bg={colors['cursor_line']}] #I #[fg={colors['cursor_line']},bg={colors['border']}]#[fg={colors['fg_bright']},bg={colors['border']}] #W #[fg={colors['border']},bg={colors['bg']}]"
|
|
"""
|
|
).strip()
|
|
|
|
|
|
def render_alacritty_toml(colors: dict[str, str]) -> str:
|
|
return dedent(
|
|
f"""
|
|
# Generated from palette.json by generate.py. Do not edit directly.
|
|
|
|
[colors.primary]
|
|
background = "{colors['bg']}"
|
|
foreground = "{colors['fg']}"
|
|
dim_foreground = "{colors['fg_gutter']}"
|
|
bright_foreground = "{colors['fg_bright']}"
|
|
|
|
[colors.cursor]
|
|
text = "{colors['bg']}"
|
|
cursor = "{colors['fg_bright']}"
|
|
|
|
[colors.vi_mode_cursor]
|
|
text = "{colors['bg']}"
|
|
cursor = "{colors['keyword']}"
|
|
|
|
[colors.selection]
|
|
text = "{colors['fg_bright']}"
|
|
background = "{colors['selection']}"
|
|
|
|
[colors.search.matches]
|
|
foreground = "{colors['fg_bright']}"
|
|
background = "{colors['border']}"
|
|
|
|
[colors.search.focused_match]
|
|
foreground = "{colors['bg']}"
|
|
background = "{colors['func']}"
|
|
|
|
[colors.hints.start]
|
|
foreground = "{colors['bg']}"
|
|
background = "{colors['string']}"
|
|
|
|
[colors.hints.end]
|
|
foreground = "{colors['fg_bright']}"
|
|
background = "{colors['cursor_line']}"
|
|
|
|
[colors.line_indicator]
|
|
foreground = "None"
|
|
background = "None"
|
|
|
|
[colors.footer_bar]
|
|
foreground = "{colors['fg_bright']}"
|
|
background = "{colors['bg']}"
|
|
|
|
[colors.normal]
|
|
black = "{colors['bg']}"
|
|
red = "{colors['error']}"
|
|
green = "{colors['func']}"
|
|
yellow = "{colors['string']}"
|
|
blue = "{colors['keyword']}"
|
|
magenta = "{colors['type']}"
|
|
cyan = "{colors['field']}"
|
|
white = "{colors['fg']}"
|
|
|
|
[colors.bright]
|
|
black = "{colors['border']}"
|
|
red = "{colors['error']}"
|
|
green = "{colors['comment']}"
|
|
yellow = "{colors['string']}"
|
|
blue = "{colors['keyword']}"
|
|
magenta = "{colors['type_alt']}"
|
|
cyan = "{colors['field']}"
|
|
white = "{colors['fg_bright']}"
|
|
|
|
[colors.dim]
|
|
black = "{colors['cursor_line']}"
|
|
red = "{colors['error']}"
|
|
green = "{colors['comment']}"
|
|
yellow = "{colors['string']}"
|
|
blue = "{colors['keyword']}"
|
|
magenta = "{colors['type']}"
|
|
cyan = "{colors['field']}"
|
|
white = "{colors['fg_gutter']}"
|
|
"""
|
|
).strip()
|
|
|
|
|
|
def render_bash_prompt() -> str:
|
|
return dedent(
|
|
"""
|
|
# Generated from palette.json by generate.py. Do not edit directly.
|
|
# Rider-colored bash prompt using the shared palette.
|
|
|
|
# shellcheck shell=bash
|
|
|
|
if [ -r "$HOME/.config/rider-palette/palette.sh" ]; then
|
|
. "$HOME/.config/rider-palette/palette.sh"
|
|
fi
|
|
|
|
rider_fg() {
|
|
local hex="${1#\\#}"
|
|
printf '\\[\\033[38;2;%d;%d;%dm\\]' "$((16#${hex:0:2}))" "$((16#${hex:2:2}))" "$((16#${hex:4:2}))"
|
|
}
|
|
|
|
RIDER_RESET='\\[\\033[0m\\]'
|
|
|
|
__rider_git_branch() {
|
|
command -v git >/dev/null 2>&1 || return 0
|
|
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 0
|
|
|
|
local branch
|
|
branch="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)" || return 0
|
|
[ -n "$branch" ] || return 0
|
|
|
|
printf ' %sgit:%s%s' "$(rider_fg "$RIDER_FUNC")" "$branch" "$RIDER_RESET"
|
|
}
|
|
|
|
__rider_set_bash_prompt() {
|
|
PS1="${debian_chroot:+($debian_chroot)}$(rider_fg "$RIDER_FG_BRIGHT")\\u@\\h${RIDER_RESET}$(rider_fg "$RIDER_BORDER"):$(rider_fg "$RIDER_KEYWORD")\\w${RIDER_RESET}$(__rider_git_branch)$(rider_fg "$RIDER_BORDER") \\\\$ ${RIDER_RESET}"
|
|
}
|
|
|
|
PROMPT_COMMAND=__rider_set_bash_prompt
|
|
"""
|
|
).strip()
|
|
|
|
|
|
def render_p10k_zsh() -> str:
|
|
return dedent(
|
|
"""
|
|
# Generated from palette.json by generate.py. Do not edit directly.
|
|
# Rider-colored Powerlevel10k overrides using the shared palette.
|
|
|
|
[[ -r "$HOME/.config/rider-palette/palette.sh" ]] && source "$HOME/.config/rider-palette/palette.sh"
|
|
|
|
typeset -g POWERLEVEL9K_BACKGROUND="$RIDER_BG"
|
|
|
|
typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX="%F{$RIDER_BORDER}╭─%f"
|
|
typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX="%F{$RIDER_BORDER}├─%f"
|
|
typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX="%F{$RIDER_BORDER}╰─%f"
|
|
typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX="%F{$RIDER_BORDER}─╮%f"
|
|
typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX="%F{$RIDER_BORDER}─┤%f"
|
|
typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX="%F{$RIDER_BORDER}─╯%f"
|
|
|
|
typeset -g POWERLEVEL9K_LEFT_SUBSEGMENT_SEPARATOR="%F{$RIDER_BORDER}\\uE0B1%f"
|
|
typeset -g POWERLEVEL9K_RIGHT_SUBSEGMENT_SEPARATOR="%F{$RIDER_BORDER}\\uE0B3%f"
|
|
|
|
typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND="$RIDER_FG_BRIGHT"
|
|
|
|
typeset -g POWERLEVEL9K_DIR_FOREGROUND="$RIDER_KEYWORD"
|
|
typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND="$RIDER_TYPE"
|
|
typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND="$RIDER_FUNC"
|
|
|
|
typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR="$RIDER_FUNC"
|
|
typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR="$RIDER_BORDER"
|
|
typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND="$RIDER_FUNC"
|
|
typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND="$RIDER_FIELD"
|
|
typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND="$RIDER_NUMBER"
|
|
|
|
typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND="$RIDER_FUNC"
|
|
typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND="$RIDER_FUNC"
|
|
typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND="$RIDER_ERROR"
|
|
typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND="$RIDER_ERROR"
|
|
typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND="$RIDER_ERROR"
|
|
|
|
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND="$RIDER_STRING"
|
|
typeset -g POWERLEVEL9K_TIME_FOREGROUND="$RIDER_FIELD"
|
|
typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND="$RIDER_TYPE"
|
|
typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND="$RIDER_FG_BRIGHT"
|
|
"""
|
|
).strip()
|
|
|
|
|
|
def render_readme() -> str:
|
|
return dedent(
|
|
"""
|
|
# Rider Palette Reuse
|
|
|
|
`palette.json` is the single source of truth for your shared Rider palette.
|
|
|
|
Regenerate all derived files with:
|
|
|
|
```bash
|
|
python3 ~/.config/rider-palette/generate.py
|
|
```
|
|
|
|
Generated outputs:
|
|
|
|
- `palette.sh`: shell env vars plus shared `LS_COLORS` for prompts and scripts
|
|
- `palette.css`: CSS variables for Waybar and Wofi
|
|
- `palette.hyprland.conf`: Hyprlang variables for Hyprland and Hyprlock
|
|
- `palette.rasi`: Rasi variables for Rofi
|
|
- `tmux.conf`: tmux status and pane colors
|
|
- `alacritty.toml`: Alacritty color theme
|
|
- `bash-prompt.sh`: bash prompt using the shared palette
|
|
- `p10k.zsh`: Powerlevel10k overrides using the shared palette
|
|
|
|
This package is meant to be stowed from the dotfiles repo so that
|
|
`~/.config/rider-palette/*` becomes available to the rest of the system.
|
|
"""
|
|
).strip()
|
|
|
|
|
|
def main() -> None:
|
|
colors = load_palette()
|
|
|
|
outputs = {
|
|
ROOT / "palette.sh": render_palette_sh(colors),
|
|
ROOT / "palette.css": render_palette_css(colors),
|
|
ROOT / "palette.hyprland.conf": render_palette_hypr(colors),
|
|
ROOT / "palette.rasi": render_palette_rasi(colors),
|
|
ROOT / "tmux.conf": render_tmux_conf(colors),
|
|
ROOT / "alacritty.toml": render_alacritty_toml(colors),
|
|
ROOT / "bash-prompt.sh": render_bash_prompt(),
|
|
ROOT / "p10k.zsh": render_p10k_zsh(),
|
|
ROOT / "README.md": render_readme(),
|
|
}
|
|
|
|
for path, content in outputs.items():
|
|
write(path, content)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|