perf: improving performance, layout (#215)
Co-authored-by: nullishamy <git@amyerskine.me>
49
.github/workflows/build.yml
vendored
@@ -3,33 +3,64 @@ name: "Generate test artifacts"
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, reopened, synchronize]
|
types: [opened, reopened, synchronize]
|
||||||
|
|
||||||
|
# env:
|
||||||
|
# TAG: latest
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# container:
|
||||||
|
# image: ghcr.io/${{ github.repository_owner }}/${{ github.repository }}:${{ env.TAG }}
|
||||||
|
#
|
||||||
|
# # is this really necessary?
|
||||||
|
# credentials:
|
||||||
|
# username: ${{ github.actor }}
|
||||||
|
# password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
cache: "pip"
|
cache: "pip"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install -r requirements.txt
|
run: |
|
||||||
- name: Install colloid specific dependencies
|
sudo apt update && sudo apt install -y sassc inkscape optipng
|
||||||
run: sudo apt update && sudo apt install -y sassc inkscape optipng
|
pip install -r requirements.txt
|
||||||
|
|
||||||
- name: Generate themes
|
- name: Generate themes
|
||||||
run: |
|
run: |
|
||||||
python patches/xfwm4/generate_assets.py
|
set -eu pipefile
|
||||||
|
|
||||||
|
python sources/patches/xfwm4/generate_assets.py
|
||||||
|
|
||||||
|
python ./build.py mocha --all-accents --zip -d $PWD/releases > mocha.log 2>&1 &
|
||||||
|
python ./build.py macchiato --all-accents --zip -d $PWD/releases > macchiato.log 2>&1 &
|
||||||
|
python ./build.py frappe --all-accents --zip -d $PWD/releases > frappe.log 2>&1 &
|
||||||
|
python ./build.py latte --all-accents --zip -d $PWD/releases > latte.log 2>&1 &
|
||||||
|
|
||||||
|
declare -i err=0 werr=0
|
||||||
|
while wait -fn || werr=$?; ((werr != 127)); do
|
||||||
|
err=$werr
|
||||||
|
if [[ $err -ne 0 ]]; then
|
||||||
|
echo "Build failure, abort"
|
||||||
|
cat *.log
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cat *.log
|
||||||
|
|
||||||
python ./build.py mocha --all-accents --zip -d $PWD/releases &&
|
|
||||||
python ./build.py macchiato --all-accents --zip -d $PWD/releases &&
|
|
||||||
python ./build.py frappe --all-accents --zip -d $PWD/releases &&
|
|
||||||
python ./build.py latte --all-accents --zip -d $PWD/releases
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: '${{ github.sha }}-artifacts'
|
name: '${{ github.sha }}-artifacts'
|
||||||
path: ./releases/*.zip
|
path: ./releases/*.zip
|
||||||
|
|
||||||
|
27
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: "Publish containers for build"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
# types: [opened, reopened, synchronize]
|
||||||
|
types: [synchronize]
|
||||||
|
|
||||||
|
permissions: write-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Generate and push image to ghcr
|
||||||
|
run: |
|
||||||
|
# build docker image for the ci
|
||||||
|
sh docker/build.sh -v no
|
||||||
|
|
||||||
|
# push the image to the registry
|
||||||
|
sh docker/push.sh -u "${{ github.actor }}" -p "${{ secrets.GITHUB_TOKEN }}"
|
28
.github/workflows/release.yml
vendored
@@ -28,22 +28,40 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
cache: "pip"
|
cache: "pip"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install -r requirements.txt
|
run: pip install -r requirements.txt
|
||||||
|
|
||||||
- name: Install colloid specific dependencies
|
- name: Install colloid specific dependencies
|
||||||
run: sudo apt update && sudo apt install -y sassc inkscape optipng
|
run: sudo apt update && sudo apt install -y sassc inkscape optipng
|
||||||
|
|
||||||
- name: Generate themes
|
- name: Generate themes
|
||||||
run: |
|
run: |
|
||||||
python patches/xfwm4/generate_assets.py
|
set -eu pipefile
|
||||||
|
|
||||||
|
python sources/patches/xfwm4/generate_assets.py
|
||||||
|
|
||||||
|
python ./build.py mocha --all-accents --zip -d $PWD/releases > mocha.log 2>&1 &
|
||||||
|
python ./build.py macchiato --all-accents --zip -d $PWD/releases > macchiato.log 2>&1 &
|
||||||
|
python ./build.py frappe --all-accents --zip -d $PWD/releases > frappe.log 2>&1 &
|
||||||
|
python ./build.py latte --all-accents --zip -d $PWD/releases > latte.log 2>&1 &
|
||||||
|
declare -i err=0 werr=0
|
||||||
|
while wait -fn || werr=$?; ((werr != 127)); do
|
||||||
|
err=$werr
|
||||||
|
if [[ $err -ne 0 ]]; then
|
||||||
|
echo "Build failure, abort"
|
||||||
|
cat *.log
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cat *.log
|
||||||
|
|
||||||
python ./build.py mocha --all-accents --zip -d $PWD/releases &&
|
|
||||||
python ./build.py macchiato --all-accents --zip -d $PWD/releases &&
|
|
||||||
python ./build.py frappe --all-accents --zip -d $PWD/releases &&
|
|
||||||
python ./build.py latte --all-accents --zip -d $PWD/releases
|
|
||||||
- name: Add zips to release
|
- name: Add zips to release
|
||||||
run: gh release upload ${{ needs.release-please.outputs.tag_name }} releases/*.zip
|
run: gh release upload ${{ needs.release-please.outputs.tag_name }} releases/*.zip
|
||||||
env:
|
env:
|
||||||
|
3
.gitignore
vendored
@@ -8,6 +8,9 @@ lib*/
|
|||||||
*.cfg
|
*.cfg
|
||||||
.direnv
|
.direnv
|
||||||
build/
|
build/
|
||||||
|
*.log
|
||||||
|
.ruff-cache
|
||||||
|
.tmp
|
||||||
|
|
||||||
# Releases folder
|
# Releases folder
|
||||||
releases
|
releases
|
||||||
|
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
|||||||
[submodule "colloid"]
|
[submodule "colloid"]
|
||||||
path = colloid
|
path = sources/colloid
|
||||||
url = https://github.com/vinceliuice/Colloid-gtk-theme.git
|
url = https://github.com/vinceliuice/Colloid-gtk-theme.git
|
||||||
|
648
build.py
@@ -1,636 +1,22 @@
|
|||||||
#!/usr/bin/env python3
|
import os
|
||||||
import os, re, shutil, subprocess, argparse, glob, logging, zipfile, sys
|
import sys
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from sources.build import execute_build
|
||||||
from typing import Any, Literal, List
|
from sources.build.args import parse_args
|
||||||
|
from sources.build.logger import logger
|
||||||
|
|
||||||
from catppuccin import PALETTE
|
if __name__ == "__main__":
|
||||||
from catppuccin.models import Flavor, Color
|
git_root = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
SRC_DIR = f"{THIS_DIR}/colloid/src"
|
|
||||||
SASSC_OPT = ["-M", "-t", "expanded"]
|
|
||||||
|
|
||||||
logger = logging.getLogger("catppuccin-gtk")
|
|
||||||
logger.setLevel(logging.DEBUG)
|
|
||||||
ch = logging.StreamHandler()
|
|
||||||
formatter = logging.Formatter("[%(name)s] [%(levelname)s] - %(message)s")
|
|
||||||
ch.setFormatter(formatter)
|
|
||||||
logger.addHandler(ch)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Tweaks:
|
|
||||||
tweaks: List[str]
|
|
||||||
|
|
||||||
def has(self, tweak: str) -> bool:
|
|
||||||
return tweak in self.tweaks
|
|
||||||
|
|
||||||
def id(self) -> str:
|
|
||||||
return ",".join(self.tweaks)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Suffix:
|
|
||||||
true_value: str
|
|
||||||
test: Any # callback function
|
|
||||||
false_value: str = ""
|
|
||||||
|
|
||||||
|
|
||||||
IS_DARK = Suffix(true_value="-Dark", test=lambda ctx: ctx.flavor.dark)
|
|
||||||
IS_LIGHT = Suffix(true_value="-Light", test=lambda ctx: not ctx.flavor.dark)
|
|
||||||
IS_WINDOW_NORMAL = Suffix(true_value="-Normal", test=lambda ctx: ctx.tweaks.has('normal'))
|
|
||||||
DARK_LIGHT = Suffix(
|
|
||||||
true_value="-Dark", false_value="-Light", test=lambda ctx: ctx.flavor.dark
|
|
||||||
)
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class BuildContext:
|
|
||||||
build_root: str
|
|
||||||
output_format: Literal["zip"] | Literal["dir"]
|
|
||||||
theme_name: str
|
|
||||||
flavor: Flavor
|
|
||||||
accent: Color
|
|
||||||
size: Literal["standard"] | Literal["compact"]
|
|
||||||
tweaks: Tweaks
|
|
||||||
|
|
||||||
def output_dir(self) -> str:
|
|
||||||
suffix = "light"
|
|
||||||
if self.flavor.dark:
|
|
||||||
suffix = "dark"
|
|
||||||
return f"{self.build_root}/{self.build_id()}-{suffix}"
|
|
||||||
|
|
||||||
def build_id(self) -> str:
|
|
||||||
return f"{self.theme_name}-{self.flavor.identifier}-{self.accent.identifier}-{self.size}+{self.tweaks.id() or 'default'}"
|
|
||||||
|
|
||||||
def apply_suffix(self, suffix: Suffix) -> str:
|
|
||||||
if suffix.test(self):
|
|
||||||
return suffix.true_value
|
|
||||||
else:
|
|
||||||
return suffix.false_value
|
|
||||||
|
|
||||||
|
|
||||||
def build(ctx: BuildContext):
|
|
||||||
output_dir = ctx.output_dir()
|
|
||||||
logger.info(f"Building into '{output_dir}'...")
|
|
||||||
|
|
||||||
apply_tweaks(ctx)
|
|
||||||
|
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
|
||||||
with open(f"{output_dir}/index.theme", "w") as file:
|
|
||||||
file.write("[Desktop Entry]\n")
|
|
||||||
file.write("Type=X-GNOME-Metatheme\n")
|
|
||||||
file.write(f"Name={ctx.build_id()}\n")
|
|
||||||
file.write("Comment=An Flat Gtk+ theme based on Elegant Design\n")
|
|
||||||
file.write("Encoding=UTF-8\n")
|
|
||||||
file.write("\n")
|
|
||||||
file.write("[X-GNOME-Metatheme]\n")
|
|
||||||
file.write(f"GtkTheme={ctx.build_id()}\n")
|
|
||||||
file.write(f"MetacityTheme={ctx.build_id()}\n")
|
|
||||||
file.write(f"IconTheme=Tela-circle{ctx.apply_suffix(IS_DARK)}\n")
|
|
||||||
file.write(f"CursorTheme={ctx.flavor.name}-cursors\n")
|
|
||||||
file.write("ButtonLayout=close,minimize,maximize:menu\n")
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}/gnome-shell", exist_ok=True)
|
|
||||||
shutil.copyfile(
|
|
||||||
f"{SRC_DIR}/main/gnome-shell/pad-osd.css",
|
|
||||||
f"{output_dir}/gnome-shell/pad-osd.css",
|
|
||||||
)
|
|
||||||
subprocess.check_call(
|
|
||||||
[
|
|
||||||
"sassc",
|
|
||||||
*SASSC_OPT,
|
|
||||||
f"{SRC_DIR}/main/gnome-shell/gnome-shell{ctx.apply_suffix(DARK_LIGHT)}.scss",
|
|
||||||
f"{output_dir}/gnome-shell/gnome-shell.css",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}/gtk-3.0", exist_ok=True)
|
|
||||||
subprocess.check_call(
|
|
||||||
[
|
|
||||||
"sassc",
|
|
||||||
*SASSC_OPT,
|
|
||||||
f"{SRC_DIR}/main/gtk-3.0/gtk{ctx.apply_suffix(DARK_LIGHT)}.scss",
|
|
||||||
f"{output_dir}/gtk-3.0/gtk.css",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
subprocess.check_call(
|
|
||||||
[
|
|
||||||
"sassc",
|
|
||||||
*SASSC_OPT,
|
|
||||||
# NOTE: This uses 'Dark' for the source, but 'dark' for the destination. This is intentional. Do !!NOT!! change it without consultation
|
|
||||||
f"{SRC_DIR}/main/gtk-3.0/gtk-Dark.scss",
|
|
||||||
f"{output_dir}/gtk-3.0/gtk-dark.css",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}/gtk-4.0", exist_ok=True)
|
|
||||||
subprocess.check_call(
|
|
||||||
[
|
|
||||||
"sassc",
|
|
||||||
*SASSC_OPT,
|
|
||||||
f"{SRC_DIR}/main/gtk-4.0/gtk{ctx.apply_suffix(DARK_LIGHT)}.scss",
|
|
||||||
f"{output_dir}/gtk-4.0/gtk.css",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
subprocess.check_call(
|
|
||||||
[
|
|
||||||
"sassc",
|
|
||||||
*SASSC_OPT,
|
|
||||||
# NOTE: This uses 'Dark' for the source, but 'dark' for the destination. This is intentional. Do !!NOT!! change it without consultation
|
|
||||||
f"{SRC_DIR}/main/gtk-4.0/gtk-Dark.scss",
|
|
||||||
f"{output_dir}/gtk-4.0/gtk-dark.css",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}/cinnamon", exist_ok=True)
|
|
||||||
subprocess.check_call(
|
|
||||||
[
|
|
||||||
"sassc",
|
|
||||||
*SASSC_OPT,
|
|
||||||
f"{SRC_DIR}/main/cinnamon/cinnamon{ctx.apply_suffix(DARK_LIGHT)}.scss",
|
|
||||||
f"{output_dir}/cinnamon/cinnamon.css",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}/metacity-1", exist_ok=True)
|
|
||||||
shutil.copyfile(
|
|
||||||
f"{SRC_DIR}/main/metacity-1/metacity-theme-3{ctx.apply_suffix(IS_WINDOW_NORMAL)}.xml",
|
|
||||||
f"{output_dir}/metacity-1/metacity-theme-3.xml",
|
|
||||||
)
|
|
||||||
# FIXME: Symlinks aren't working as intended
|
|
||||||
# FIXME: Do we need them?
|
|
||||||
# os.symlink(
|
|
||||||
# f"{output_dir}/metacity-1/metacity-theme-3.xml",
|
|
||||||
# f"{output_dir}/metacity-1/metacity-theme-2.xml",
|
|
||||||
# )
|
|
||||||
# os.symlink(
|
|
||||||
# f"{output_dir}/metacity-1/metacity-theme-3.xml",
|
|
||||||
# f"{output_dir}/metacity-1/metacity-theme-1.xml",
|
|
||||||
# )
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}/xfwm4", exist_ok=True)
|
|
||||||
shutil.copyfile(
|
|
||||||
f"{SRC_DIR}/main/xfwm4/themerc{ctx.apply_suffix(IS_LIGHT)}",
|
|
||||||
f"{output_dir}/xfwm4/themerc",
|
|
||||||
)
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}-hdpi/xfwm4", exist_ok=True)
|
|
||||||
shutil.copyfile(
|
|
||||||
f"{SRC_DIR}/main/xfwm4/themerc{ctx.apply_suffix(IS_LIGHT)}",
|
|
||||||
f"{output_dir}-hdpi/xfwm4/themerc",
|
|
||||||
)
|
|
||||||
subst_text(f"{output_dir}-hdpi/xfwm4/themerc", "button_offset=6", "button_offset=9")
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}-xhdpi/xfwm4", exist_ok=True)
|
|
||||||
shutil.copyfile(
|
|
||||||
f"{SRC_DIR}/main/xfwm4/themerc{ctx.apply_suffix(IS_LIGHT)}",
|
|
||||||
f"{output_dir}-xhdpi/xfwm4/themerc",
|
|
||||||
)
|
|
||||||
subst_text(
|
|
||||||
f"{output_dir}-xhdpi/xfwm4/themerc", "button_offset=6", "button_offset=12"
|
|
||||||
)
|
|
||||||
|
|
||||||
if not ctx.flavor.dark:
|
|
||||||
shutil.copytree(
|
|
||||||
f"{SRC_DIR}/main/plank/theme-Light-Catppuccin/", f"{output_dir}/plank", dirs_exist_ok=True
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
shutil.copytree(
|
|
||||||
f"{SRC_DIR}/main/plank/theme-Dark-Catppuccin/", f"{output_dir}/plank", dirs_exist_ok=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def init_tweaks_temp():
|
|
||||||
shutil.copyfile(f"{SRC_DIR}/sass/_tweaks.scss", f"{SRC_DIR}/sass/_tweaks-temp.scss")
|
|
||||||
|
|
||||||
|
|
||||||
def subst_text(path, _from, to):
|
|
||||||
with open(path, "r+") as f:
|
|
||||||
content = f.read()
|
|
||||||
f.seek(0)
|
|
||||||
f.truncate()
|
|
||||||
f.write(re.sub(_from, to, content))
|
|
||||||
|
|
||||||
|
|
||||||
GS_VERSION = "46-0"
|
|
||||||
|
|
||||||
def gnome_shell_version():
|
|
||||||
shutil.copyfile(
|
|
||||||
f"{SRC_DIR}/sass/gnome-shell/_common.scss",
|
|
||||||
f"{SRC_DIR}/sass/gnome-shell/_common-temp.scss",
|
|
||||||
)
|
|
||||||
subst_text(
|
|
||||||
f"{SRC_DIR}/sass/gnome-shell/_common-temp.scss",
|
|
||||||
"@import 'widgets-40-0';",
|
|
||||||
f"@import 'widgets-{GS_VERSION}';",
|
|
||||||
)
|
|
||||||
|
|
||||||
if GS_VERSION == "3-28":
|
|
||||||
subst_text(
|
|
||||||
f"{SRC_DIR}/sass/gnome-shell/_common-temp.scss",
|
|
||||||
"@import 'extensions-40-0';",
|
|
||||||
f"@import 'extensions-{GS_VERSION}';",
|
|
||||||
)
|
|
||||||
|
|
||||||
def write_tweak(key, default, value):
|
|
||||||
subst_text(
|
|
||||||
f"{SRC_DIR}/sass/_tweaks-temp.scss", f"\\${key}: {default}", f"${key}: {value}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def apply_tweaks(ctx: BuildContext):
|
|
||||||
write_tweak("theme", "'default'", f"'{ctx.accent.identifier}'")
|
|
||||||
|
|
||||||
if ctx.size == "compact":
|
|
||||||
write_tweak("compact", "'false'", "'true'")
|
|
||||||
|
|
||||||
subst_text(
|
|
||||||
f"{SRC_DIR}/sass/_tweaks-temp.scss",
|
|
||||||
"@import 'color-palette-default';",
|
|
||||||
f"@import 'color-palette-catppuccin-{ctx.flavor.identifier}';",
|
|
||||||
)
|
|
||||||
write_tweak("colorscheme", "'default'", "'catppuccin'")
|
|
||||||
|
|
||||||
if ctx.tweaks.has("black"):
|
|
||||||
write_tweak("blackness", "'false'", "'true'")
|
|
||||||
|
|
||||||
if ctx.tweaks.has("rimless"):
|
|
||||||
write_tweak("rimless", "'false'", "'true'")
|
|
||||||
|
|
||||||
if ctx.tweaks.has("normal"):
|
|
||||||
write_tweak("window_button", "'mac'", "'normal'")
|
|
||||||
|
|
||||||
if ctx.tweaks.has("float"):
|
|
||||||
write_tweak("float", "'false'", "'true'")
|
|
||||||
|
|
||||||
|
|
||||||
def make_assets(ctx: BuildContext):
|
|
||||||
output_dir = ctx.output_dir()
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}/cinnamon/assets", exist_ok=True)
|
|
||||||
for file in glob.glob(f"{SRC_DIR}/assets/cinnamon/theme/*.svg"):
|
|
||||||
shutil.copy(file, f"{output_dir}/cinnamon/assets")
|
|
||||||
shutil.copy(
|
|
||||||
f"{SRC_DIR}/assets/cinnamon/thumbnail{ctx.apply_suffix(DARK_LIGHT)}.svg",
|
|
||||||
f"{output_dir}/cinnamon/thumbnail.png",
|
|
||||||
)
|
|
||||||
|
|
||||||
os.makedirs(f"{output_dir}/gnome-shell/assets", exist_ok=True)
|
|
||||||
for file in glob.glob(f"{SRC_DIR}/assets/gnome-shell/theme/*.svg"):
|
|
||||||
shutil.copy(file, f"{output_dir}/gnome-shell/assets")
|
|
||||||
|
|
||||||
shutil.copytree(
|
|
||||||
f"{SRC_DIR}/assets/gtk/assets",
|
|
||||||
f"{output_dir}/gtk-3.0/assets",
|
|
||||||
dirs_exist_ok=True,
|
|
||||||
)
|
|
||||||
shutil.copytree(
|
|
||||||
f"{SRC_DIR}/assets/gtk/assets",
|
|
||||||
f"{output_dir}/gtk-4.0/assets",
|
|
||||||
dirs_exist_ok=True,
|
|
||||||
)
|
|
||||||
shutil.copyfile(
|
|
||||||
f"{SRC_DIR}/assets/gtk/thumbnail{ctx.apply_suffix(IS_DARK)}.svg",
|
|
||||||
f"{output_dir}/gtk-3.0/thumbnail.png",
|
|
||||||
)
|
|
||||||
shutil.copyfile(
|
|
||||||
f"{SRC_DIR}/assets/gtk/thumbnail{ctx.apply_suffix(IS_DARK)}.svg",
|
|
||||||
f"{output_dir}/gtk-4.0/thumbnail.png",
|
|
||||||
)
|
|
||||||
|
|
||||||
theme_color = ctx.accent.hex
|
|
||||||
|
|
||||||
palette = ctx.flavor.colors
|
|
||||||
background = palette.base.hex
|
|
||||||
background_alt = palette.mantle.hex
|
|
||||||
titlebar = palette.overlay0.hex
|
|
||||||
|
|
||||||
for file in glob.glob(f"{output_dir}/cinnamon/assets/*.svg"):
|
|
||||||
subst_text(file, "#5b9bf8", theme_color)
|
|
||||||
subst_text(file, "#3c84f7", theme_color)
|
|
||||||
|
|
||||||
for file in glob.glob(f"{output_dir}/gnome-shell/assets/*.svg"):
|
|
||||||
subst_text(file, "#5b9bf8", theme_color)
|
|
||||||
subst_text(file, "#3c84f7", theme_color)
|
|
||||||
|
|
||||||
for file in glob.glob(f"{output_dir}/gtk-3.0/assets/*.svg"):
|
|
||||||
subst_text(file, "#5b9bf8", theme_color)
|
|
||||||
subst_text(file, "#3c84f7", theme_color)
|
|
||||||
subst_text(file, "#ffffff", background)
|
|
||||||
subst_text(file, "#2c2c2c", background)
|
|
||||||
subst_text(file, "#3c3c3c", background_alt)
|
|
||||||
|
|
||||||
for file in glob.glob(f"{output_dir}/gtk-4.0/assets/*.svg"):
|
|
||||||
subst_text(file, "#5b9bf8", theme_color)
|
|
||||||
subst_text(file, "#3c84f7", theme_color)
|
|
||||||
subst_text(file, "#ffffff", background)
|
|
||||||
subst_text(file, "#2c2c2c", background)
|
|
||||||
subst_text(file, "#3c3c3c", background_alt)
|
|
||||||
|
|
||||||
if ctx.flavor.dark:
|
|
||||||
subst_text(f"{output_dir}/cinnamon/thumbnail.png", "#2c2c2c", background)
|
|
||||||
subst_text(f"{output_dir}/cinnamon/thumbnail.png", "#5b9bf8", theme_color)
|
|
||||||
|
|
||||||
subst_text(f"{output_dir}/gtk-3.0/thumbnail.png", "#2c2c2c", background)
|
|
||||||
subst_text(f"{output_dir}/gtk-4.0/thumbnail.png", "#2c2c2c", background)
|
|
||||||
|
|
||||||
subst_text(f"{output_dir}/gtk-3.0/thumbnail.png", "#5b9bf8", theme_color)
|
|
||||||
subst_text(f"{output_dir}/gtk-4.0/thumbnail.png", "#5b9bf8", theme_color)
|
|
||||||
else:
|
|
||||||
subst_text(f"{output_dir}/cinnamon/thumbnail.png", "#ffffff", background)
|
|
||||||
subst_text(f"{output_dir}/cinnamon/thumbnail.png", "#f2f2f2", titlebar)
|
|
||||||
subst_text(f"{output_dir}/cinnamon/thumbnail.png", "#3c84f7", theme_color)
|
|
||||||
|
|
||||||
subst_text(f"{output_dir}/gtk-3.0/thumbnail.png", "#f2f2f2", titlebar)
|
|
||||||
subst_text(f"{output_dir}/gtk-3.0/thumbnail.png", "#3c84f7", theme_color)
|
|
||||||
|
|
||||||
subst_text(f"{output_dir}/gtk-4.0/thumbnail.png", "#f2f2f2", titlebar)
|
|
||||||
subst_text(f"{output_dir}/gtk-4.0/thumbnail.png", "#3c84f7", theme_color)
|
|
||||||
|
|
||||||
for file in glob.glob(f"{SRC_DIR}/assets/cinnamon/common-assets/*.svg"):
|
|
||||||
shutil.copy(file, f"{output_dir}/cinnamon/assets")
|
|
||||||
|
|
||||||
for file in glob.glob(f"{SRC_DIR}/assets/cinnamon/assets{ctx.apply_suffix(IS_DARK)}/*.svg"):
|
|
||||||
shutil.copy(file, f"{output_dir}/cinnamon/assets")
|
|
||||||
|
|
||||||
for file in glob.glob(f"{SRC_DIR}/assets/gnome-shell/common-assets/*.svg"):
|
|
||||||
shutil.copy(file, f"{output_dir}/gnome-shell/assets")
|
|
||||||
|
|
||||||
for file in glob.glob(f"{SRC_DIR}/assets/gnome-shell/assets{ctx.apply_suffix(IS_DARK)}/*.svg"):
|
|
||||||
shutil.copy(file, f"{output_dir}/gnome-shell/assets")
|
|
||||||
|
|
||||||
for file in glob.glob(f"{SRC_DIR}/assets/gtk/symbolics/*.svg"):
|
|
||||||
shutil.copy(file, f"{output_dir}/gtk-3.0/assets")
|
|
||||||
shutil.copy(file, f"{output_dir}/gtk-4.0/assets")
|
|
||||||
|
|
||||||
for file in glob.glob(f"{SRC_DIR}/assets/metacity-1/assets{ctx.apply_suffix(IS_WINDOW_NORMAL)}/*.svg"):
|
|
||||||
shutil.copy(file, f"{output_dir}/metacity-1/assets")
|
|
||||||
shutil.copy(
|
|
||||||
f"{SRC_DIR}/assets/metacity-1/thumbnail{ctx.apply_suffix(IS_DARK)}.png",
|
|
||||||
f"{output_dir}/metacity-1/thumbnail.png",
|
|
||||||
)
|
|
||||||
|
|
||||||
xfwm4_assets_root = f"{THIS_DIR}/patches/xfwm4/generated/assets-catppuccin-{ctx.flavor.identifier}"
|
|
||||||
for file in glob.glob(xfwm4_assets_root + '/*'):
|
|
||||||
shutil.copy(file, f"{output_dir}/xfwm4")
|
|
||||||
|
|
||||||
xfwm4_assets = xfwm4_assets_root + "-hdpi/*"
|
|
||||||
for file in glob.glob(xfwm4_assets):
|
|
||||||
shutil.copy(file, f"{output_dir}-hdpi/xfwm4")
|
|
||||||
|
|
||||||
xfwm4_assets = xfwm4_assets_root + "-xhdpi/*"
|
|
||||||
for file in glob.glob(xfwm4_assets):
|
|
||||||
shutil.copy(file, f"{output_dir}-xhdpi/xfwm4")
|
|
||||||
|
|
||||||
|
|
||||||
def zip_dir(path, zip_file):
|
|
||||||
# Ref: https://stackoverflow.com/questions/46229764/python-zip-multiple-directories-into-one-zip-file
|
|
||||||
for root, _, files in os.walk(path):
|
|
||||||
for file in files:
|
|
||||||
zip_file.write(
|
|
||||||
os.path.join(root, file),
|
|
||||||
os.path.relpath(os.path.join(root, file), os.path.join(path, "..")),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def zip_artifacts(dir_list, zip_name, remove=True):
|
|
||||||
with zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED) as zipf:
|
|
||||||
for dir in dir_list:
|
|
||||||
zip_dir(dir, zipf)
|
|
||||||
|
|
||||||
if remove:
|
|
||||||
for dir in dir_list:
|
|
||||||
shutil.rmtree(dir)
|
|
||||||
|
|
||||||
|
|
||||||
def build_theme(ctx: BuildContext):
|
|
||||||
build_info = f"""Build info:
|
|
||||||
build_root: {ctx.build_root}
|
|
||||||
theme_name: {ctx.theme_name}
|
|
||||||
flavor: {ctx.flavor.identifier}
|
|
||||||
accent: {ctx.accent.identifier}
|
|
||||||
size: {ctx.size}
|
|
||||||
tweaks: {ctx.tweaks}"""
|
|
||||||
logger.info(build_info)
|
|
||||||
build(ctx)
|
|
||||||
logger.info(f"Main build complete")
|
|
||||||
|
|
||||||
logger.info("Bundling assets...")
|
|
||||||
make_assets(ctx)
|
|
||||||
logger.info("Asset bundling done")
|
|
||||||
|
|
||||||
if ctx.output_format == "zip":
|
|
||||||
zip_artifacts(
|
|
||||||
[
|
|
||||||
ctx.output_dir(),
|
|
||||||
f"{ctx.output_dir()}-hdpi",
|
|
||||||
f"{ctx.output_dir()}-xhdpi",
|
|
||||||
],
|
|
||||||
f"{ctx.build_root}/{ctx.build_id()}.zip",
|
|
||||||
True,
|
|
||||||
)
|
|
||||||
|
|
||||||
"""
|
|
||||||
if (command -v xfce4-popup-whiskermenu &> /dev/null) && $(sed -i "s|.*menu-opacity=.*|menu-opacity=95|" "$HOME/.config/xfce4/panel/whiskermenu"*".rc" &> /dev/null); then
|
|
||||||
sed -i "s|.*menu-opacity=.*|menu-opacity=95|" "$HOME/.config/xfce4/panel/whiskermenu"*".rc"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if (pgrep xfce4-session &> /dev/null); then
|
|
||||||
xfce4-panel -r
|
|
||||||
fi
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def apply_colloid_patches():
|
|
||||||
if os.path.isfile("colloid/.patched"):
|
|
||||||
logger.info(
|
|
||||||
'Patches seem to be applied, remove "colloid/.patched" to force application (this may fail)'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info("Applying patches...")
|
|
||||||
# Change into colloid
|
|
||||||
for patch in [
|
|
||||||
"plank-dark.patch",
|
|
||||||
"plank-light.patch",
|
|
||||||
"theme-func.patch",
|
|
||||||
"sass-palette-frappe.patch",
|
|
||||||
"sass-palette-mocha.patch",
|
|
||||||
"sass-palette-latte.patch",
|
|
||||||
"sass-palette-macchiato.patch",
|
|
||||||
"fixes/alt-tab-background-color.patch"
|
|
||||||
]:
|
|
||||||
path = f"./patches/colloid/{patch}"
|
|
||||||
logger.info(f"Applying patch '{patch}', located at '{path}'")
|
|
||||||
subprocess.check_call(["git", "apply", path, "--directory", f"colloid"])
|
|
||||||
|
|
||||||
with open("colloid/.patched", "w") as f:
|
|
||||||
f.write("true")
|
|
||||||
|
|
||||||
logger.info("Patching finished.")
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument(
|
|
||||||
"flavor",
|
|
||||||
type=str,
|
|
||||||
choices=["mocha", "frappe", "macchiato", "latte"],
|
|
||||||
help="Flavor of the theme to apply.",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--name",
|
|
||||||
"-n",
|
|
||||||
type=str,
|
|
||||||
default="catppuccin",
|
|
||||||
dest="name",
|
|
||||||
help="Name of the theme to apply.",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--dest",
|
|
||||||
"-d",
|
|
||||||
type=str,
|
|
||||||
required=True,
|
|
||||||
dest="dest",
|
|
||||||
help="Destination of the files.",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--accent",
|
|
||||||
"-a",
|
|
||||||
type=str,
|
|
||||||
default="mauve",
|
|
||||||
nargs='+',
|
|
||||||
dest="accents",
|
|
||||||
choices=[
|
|
||||||
"rosewater",
|
|
||||||
"flamingo",
|
|
||||||
"pink",
|
|
||||||
"mauve",
|
|
||||||
"red",
|
|
||||||
"maroon",
|
|
||||||
"peach",
|
|
||||||
"yellow",
|
|
||||||
"green",
|
|
||||||
"teal",
|
|
||||||
"sky",
|
|
||||||
"sapphire",
|
|
||||||
"blue",
|
|
||||||
"lavender",
|
|
||||||
],
|
|
||||||
help="Accent of the theme.",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--all-accents",
|
|
||||||
help="Whether to build all accents",
|
|
||||||
dest="all_accents",
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--size",
|
|
||||||
"-s",
|
|
||||||
type=str,
|
|
||||||
default="standard",
|
|
||||||
dest="size",
|
|
||||||
choices=["standard", "compact"],
|
|
||||||
help="Size variant of the theme.",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--tweaks",
|
|
||||||
type=str,
|
|
||||||
default=[],
|
|
||||||
nargs="+",
|
|
||||||
dest="tweaks",
|
|
||||||
choices=["black", "rimless", "normal", "float"],
|
|
||||||
help="Tweaks to apply to the build.",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--zip",
|
|
||||||
help="Whether to bundle the theme into a zip",
|
|
||||||
type=bool,
|
|
||||||
default=False,
|
|
||||||
action=argparse.BooleanOptionalAction,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--patch",
|
|
||||||
help="Whether to patch the colloid submodule",
|
|
||||||
type=bool,
|
|
||||||
default=True,
|
|
||||||
action=argparse.BooleanOptionalAction,
|
|
||||||
)
|
|
||||||
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
if args.patch:
|
|
||||||
apply_colloid_patches()
|
|
||||||
|
|
||||||
if args.zip:
|
try:
|
||||||
output_format = "zip"
|
start = time.time()
|
||||||
else:
|
execute_build(git_root, args)
|
||||||
output_format = "dir"
|
end = time.time() - start
|
||||||
|
|
||||||
tweaks = Tweaks(tweaks=args.tweaks)
|
logger.info("")
|
||||||
|
logger.info(f"Built in {round(end, 3)}s")
|
||||||
palette = getattr(PALETTE, args.flavor)
|
except Exception as e:
|
||||||
|
logger.error("Something went wrong when building the theme:", exc_info=e)
|
||||||
accents = args.accents
|
|
||||||
if args.all_accents:
|
|
||||||
accents = [
|
|
||||||
"rosewater",
|
|
||||||
"flamingo",
|
|
||||||
"pink",
|
|
||||||
"mauve",
|
|
||||||
"red",
|
|
||||||
"maroon",
|
|
||||||
"peach",
|
|
||||||
"yellow",
|
|
||||||
"green",
|
|
||||||
"teal",
|
|
||||||
"sky",
|
|
||||||
"sapphire",
|
|
||||||
"blue",
|
|
||||||
"lavender",
|
|
||||||
]
|
|
||||||
|
|
||||||
for accent in accents:
|
|
||||||
accent = getattr(palette.colors, accent)
|
|
||||||
|
|
||||||
tweaks = Tweaks(tweaks=args.tweaks)
|
|
||||||
|
|
||||||
if args.zip:
|
|
||||||
output_format = "zip"
|
|
||||||
else:
|
|
||||||
output_format = "dir"
|
|
||||||
ctx = BuildContext(
|
|
||||||
build_root=args.dest,
|
|
||||||
theme_name=args.name,
|
|
||||||
flavor=palette,
|
|
||||||
accent=accent,
|
|
||||||
size=args.size,
|
|
||||||
tweaks=tweaks,
|
|
||||||
output_format=output_format,
|
|
||||||
)
|
|
||||||
logger.info("Building temp tweaks file")
|
|
||||||
init_tweaks_temp()
|
|
||||||
logger.info("Inserting gnome-shell imports")
|
|
||||||
gnome_shell_version()
|
|
||||||
logger.info("Building main theme")
|
|
||||||
build_theme(ctx)
|
|
||||||
logger.info(f"Completed {palette.identifier} with {accent.identifier}")
|
|
||||||
|
|
||||||
logger.info("Done!")
|
|
||||||
|
|
||||||
try:
|
|
||||||
main()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Something went wrong when building the theme:", exc_info=e)
|
|
||||||
sys.exit(1)
|
|
||||||
|
14
docker/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# This dockerfile generates a container with the needed dependencies to build the theme
|
||||||
|
|
||||||
|
FROM python:alpine
|
||||||
|
|
||||||
|
RUN apk add sassc inkscape optipng
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt && rm requirements.txt
|
||||||
|
|
||||||
|
# The reason for this is to allow the GH Actions Workflow execute commands within the container
|
||||||
|
CMD ["sleep", "infinity"]
|
83
docker/build.sh
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Default value for custom_version
|
||||||
|
VERSION="no"
|
||||||
|
|
||||||
|
while getopts v:h opt 2>/dev/null; do
|
||||||
|
case "$opt" in
|
||||||
|
v)
|
||||||
|
VERSION=$OPTARG
|
||||||
|
;;
|
||||||
|
h)
|
||||||
|
echo "\
|
||||||
|
Usage: $0 [-v <version>]
|
||||||
|
|
||||||
|
Push script for Catppuccin's GTK docker build image
|
||||||
|
|
||||||
|
-v Custom version to build the image (<your-image-name>:<version>)
|
||||||
|
If you only want to generate the image with tag 'latest' use '-v no'
|
||||||
|
-h Print this help text" >&2
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
?)
|
||||||
|
echo "Usage: $0 [-h]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [ $# -eq 0 ]
|
||||||
|
then
|
||||||
|
echo "Usage: $0 [-h]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Resolve the absolute path of the script without readlink
|
||||||
|
SCRIPT_PATH="$0"
|
||||||
|
|
||||||
|
# Check if SCRIPT_PATH is a symbolic link
|
||||||
|
while [ -h "$SCRIPT_PATH" ]; do
|
||||||
|
LS=$(ls -ld "$SCRIPT_PATH")
|
||||||
|
LINK=$(expr "$LS" : '.*-> \(.*\)$')
|
||||||
|
if expr "$LINK" : '/.*' > /dev/null; then
|
||||||
|
SCRIPT_PATH="$LINK"
|
||||||
|
else
|
||||||
|
SCRIPT_PATH=$(dirname "$SCRIPT_PATH")/"$LINK"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Ensure we have an absolute path
|
||||||
|
case "$SCRIPT_PATH" in
|
||||||
|
/*) ;;
|
||||||
|
*) SCRIPT_PATH="$(pwd)/$SCRIPT_PATH" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Path to script' dir
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "$SCRIPT_PATH")" && pwd)
|
||||||
|
|
||||||
|
# Path to the Dockerfile
|
||||||
|
DOCKERFILE_PATH="$SCRIPT_DIR/Dockerfile"
|
||||||
|
|
||||||
|
# Docker variables
|
||||||
|
IMAGE_NAME="ghcr.io/catppuccin/gtk"
|
||||||
|
|
||||||
|
# Detect podman
|
||||||
|
if command -v podman > /dev/null 2>&1; then
|
||||||
|
CONTAINER_TOOL="podman"
|
||||||
|
elif command -v docker > /dev/null 2>&1; then
|
||||||
|
CONTAINER_TOOL="docker"
|
||||||
|
else
|
||||||
|
echo "Error: Neither podman nor docker is installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean previous generated images
|
||||||
|
$CONTAINER_TOOL image rm "$IMAGE_NAME:latest" 2> /dev/null
|
||||||
|
$CONTAINER_TOOL image rm "$IMAGE_NAME:$VERSION" 2> /dev/null
|
||||||
|
|
||||||
|
# Build the Docker image with latest tag
|
||||||
|
$CONTAINER_TOOL build -t "$IMAGE_NAME:latest" -f "$DOCKERFILE_PATH" "$SCRIPT_DIR/.."
|
||||||
|
|
||||||
|
# Execute docker tag command if VERSION is not "no"
|
||||||
|
if [ "$VERSION" != "no" ]; then
|
||||||
|
$CONTAINER_TOOL tag "$IMAGE_NAME:latest" "$IMAGE_NAME:$VERSION"
|
||||||
|
fi
|
66
docker/push.sh
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Default value for parameters
|
||||||
|
VERSION="no"
|
||||||
|
USERNAME="no"
|
||||||
|
PASSWORD="no"
|
||||||
|
|
||||||
|
while getopts u:p:v:h opt 2>/dev/null; do
|
||||||
|
case "$opt" in
|
||||||
|
v)
|
||||||
|
VERSION=$OPTARG
|
||||||
|
;;
|
||||||
|
u)
|
||||||
|
USERNAME=$OPTARG
|
||||||
|
;;
|
||||||
|
p)
|
||||||
|
PASSWORD=$OPTARG
|
||||||
|
;;
|
||||||
|
h)
|
||||||
|
echo "\
|
||||||
|
Usage: $0 [-v <version> | -u [your-github-username] | -p [your-github-password]]
|
||||||
|
|
||||||
|
Push script for Catppuccin's GTK docker build image
|
||||||
|
|
||||||
|
-v Custom version to push the image (<your-image-name>:<version>)
|
||||||
|
-u Your GitHub username that will be used to log into GHCR
|
||||||
|
-p Your GitHub password that will be used to log into GHCR
|
||||||
|
-h Print this help text" >&2
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
?)
|
||||||
|
echo "Usage: $0 [-h]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [ $# -eq 0 ]
|
||||||
|
then
|
||||||
|
echo "Usage: $0 [-h]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Detect podman
|
||||||
|
if command -v podman > /dev/null 2>&1; then
|
||||||
|
CONTAINER_TOOL="podman"
|
||||||
|
elif command -v docker > /dev/null 2>&1; then
|
||||||
|
CONTAINER_TOOL="docker"
|
||||||
|
else
|
||||||
|
echo "Error: Neither podman nor docker is installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Docker variables
|
||||||
|
IMAGE_NAME="ghcr.io/catppuccin/gtk"
|
||||||
|
|
||||||
|
# Log into ghcr
|
||||||
|
$CONTAINER_TOOL login ghcr.io -u $USERNAME --password $PASSWORD
|
||||||
|
|
||||||
|
# Push docker image with latest tag
|
||||||
|
$CONTAINER_TOOL push "$IMAGE_NAME:latest"
|
||||||
|
|
||||||
|
# Execute docker push for specific version if VERSION is not "no"
|
||||||
|
if [ "$VERSION" != "no" ]; then
|
||||||
|
$CONTAINER_TOOL push "$IMAGE_NAME:$VERSION"
|
||||||
|
fi
|
@@ -6,6 +6,18 @@ Information regarding the architecture of the project can be found [in the ARCHI
|
|||||||
- All the [requirements for building](#building)
|
- All the [requirements for building](#building)
|
||||||
- `whiskers`, optionally, from [catppuccin/toolbox](https://github.com/catppuccin/toolbox/tree/main/whiskers#installation)
|
- `whiskers`, optionally, from [catppuccin/toolbox](https://github.com/catppuccin/toolbox/tree/main/whiskers#installation)
|
||||||
|
|
||||||
|
### Project structure
|
||||||
|
|
||||||
|
`sources/` contains all of the source files needed for the project to build, including the Colloid submodule.
|
||||||
|
It also contains our patches for Colloid, alongside the core build system implemented by us to replace the one from Colloid.
|
||||||
|
|
||||||
|
`build.py` is the entrypoint to the build system, placed at the root for convenience. The plumbing this utilizes is in
|
||||||
|
`sources/build`.
|
||||||
|
|
||||||
|
`install.py` is our officially supported install script, which will automate the process of pulling the release, extracting it,
|
||||||
|
and optionally adding symlinks for GTK-4.0 support. This script intentionally has no dependencies other than Python 3 itself.
|
||||||
|
This keeps the end user experience simple and reproducible. Do not add external dependencies to this script.
|
||||||
|
|
||||||
### Patching colloid
|
### Patching colloid
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> If you need to change the patches, reset the submodule and rerun the build script.
|
> If you need to change the patches, reset the submodule and rerun the build script.
|
10
install.py
@@ -1,11 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import os, zipfile, argparse, logging, io
|
import os
|
||||||
|
import zipfile
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import io
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from urllib.request import urlopen, Request
|
from urllib.request import urlopen, Request
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
logger = logging.getLogger("catppuccin-gtk")
|
logger = logging.getLogger("catppuccin-gtk")
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
@@ -160,6 +164,7 @@ def install(ctx: InstallContext):
|
|||||||
if ctx.link:
|
if ctx.link:
|
||||||
add_libadwaita_links(ctx)
|
add_libadwaita_links(ctx)
|
||||||
|
|
||||||
|
|
||||||
def install_from_artifact(ctx: InstallContext, artifact_path: Path):
|
def install_from_artifact(ctx: InstallContext, artifact_path: Path):
|
||||||
# Working from a pull request, special case it
|
# Working from a pull request, special case it
|
||||||
logger.info(f"Extracting artifact from '{artifact_path}'")
|
logger.info(f"Extracting artifact from '{artifact_path}'")
|
||||||
@@ -189,6 +194,7 @@ def install_from_artifact(ctx: InstallContext, artifact_path: Path):
|
|||||||
add_libadwaita_links(ctx, True)
|
add_libadwaita_links(ctx, True)
|
||||||
logger.info("Links added")
|
logger.info("Links added")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
||||||
|
@@ -9,5 +9,6 @@ pkgs.mkShell {
|
|||||||
sassc
|
sassc
|
||||||
inkscape
|
inkscape
|
||||||
optipng
|
optipng
|
||||||
|
ruff
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
74
sources/build/__init__.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
from argparse import Namespace
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from .patches import apply_colloid_patches
|
||||||
|
from .theme import build_with_context, gnome_shell_version
|
||||||
|
from .utils import init_tweaks_temp
|
||||||
|
from .context import Tweaks, BuildContext
|
||||||
|
from .logger import logger
|
||||||
|
from catppuccin import PALETTE
|
||||||
|
|
||||||
|
|
||||||
|
def execute_build(git_root: str, args: Namespace):
|
||||||
|
|
||||||
|
colloid_dir = f"{git_root}/sources/colloid"
|
||||||
|
colloid_tmp_dir = f"{git_root}/.tmp/colloid-tmp-{args.flavor}"
|
||||||
|
shutil.copytree(colloid_dir, colloid_tmp_dir)
|
||||||
|
src_dir = colloid_tmp_dir + "/src"
|
||||||
|
|
||||||
|
tweaks = Tweaks(tweaks=args.tweaks)
|
||||||
|
palette = getattr(PALETTE, args.flavor)
|
||||||
|
output_format = "zip" if args.zip else "dir"
|
||||||
|
|
||||||
|
if args.patch:
|
||||||
|
patch_dir = git_root + "/sources/patches/colloid/"
|
||||||
|
apply_colloid_patches(colloid_tmp_dir, patch_dir)
|
||||||
|
|
||||||
|
accents = args.accents
|
||||||
|
if args.all_accents:
|
||||||
|
accents = [
|
||||||
|
"rosewater",
|
||||||
|
"flamingo",
|
||||||
|
"pink",
|
||||||
|
"mauve",
|
||||||
|
"red",
|
||||||
|
"maroon",
|
||||||
|
"peach",
|
||||||
|
"yellow",
|
||||||
|
"green",
|
||||||
|
"teal",
|
||||||
|
"sky",
|
||||||
|
"sapphire",
|
||||||
|
"blue",
|
||||||
|
"lavender",
|
||||||
|
]
|
||||||
|
|
||||||
|
for accent in accents:
|
||||||
|
accent = getattr(palette.colors, accent)
|
||||||
|
|
||||||
|
ctx = BuildContext(
|
||||||
|
output_root=args.dest,
|
||||||
|
colloid_src_dir=src_dir,
|
||||||
|
git_root=git_root,
|
||||||
|
theme_name=args.name,
|
||||||
|
flavor=palette,
|
||||||
|
accent=accent,
|
||||||
|
size=args.size,
|
||||||
|
tweaks=tweaks,
|
||||||
|
output_format=output_format,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Building temp tweaks file")
|
||||||
|
init_tweaks_temp(src_dir)
|
||||||
|
|
||||||
|
logger.info("Inserting gnome-shell imports")
|
||||||
|
gnome_shell_version(src_dir)
|
||||||
|
|
||||||
|
logger.info("Building main theme")
|
||||||
|
build_with_context(ctx)
|
||||||
|
|
||||||
|
logger.info(f"Completed {palette.identifier} with {accent.identifier}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
shutil.rmtree(colloid_tmp_dir)
|
||||||
|
logger.info("Done!")
|
100
sources/build/args.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"flavor",
|
||||||
|
type=str,
|
||||||
|
choices=["mocha", "frappe", "macchiato", "latte"],
|
||||||
|
help="Flavor of the theme to apply.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--name",
|
||||||
|
"-n",
|
||||||
|
type=str,
|
||||||
|
default="catppuccin",
|
||||||
|
dest="name",
|
||||||
|
help="Name of the theme to apply.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--dest",
|
||||||
|
"-d",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
dest="dest",
|
||||||
|
help="Destination of the files.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--accent",
|
||||||
|
"-a",
|
||||||
|
type=str,
|
||||||
|
default="mauve",
|
||||||
|
nargs="+",
|
||||||
|
dest="accents",
|
||||||
|
choices=[
|
||||||
|
"rosewater",
|
||||||
|
"flamingo",
|
||||||
|
"pink",
|
||||||
|
"mauve",
|
||||||
|
"red",
|
||||||
|
"maroon",
|
||||||
|
"peach",
|
||||||
|
"yellow",
|
||||||
|
"green",
|
||||||
|
"teal",
|
||||||
|
"sky",
|
||||||
|
"sapphire",
|
||||||
|
"blue",
|
||||||
|
"lavender",
|
||||||
|
],
|
||||||
|
help="Accent of the theme.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--all-accents",
|
||||||
|
help="Whether to build all accents",
|
||||||
|
dest="all_accents",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--size",
|
||||||
|
"-s",
|
||||||
|
type=str,
|
||||||
|
default="standard",
|
||||||
|
dest="size",
|
||||||
|
choices=["standard", "compact"],
|
||||||
|
help="Size variant of the theme.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--tweaks",
|
||||||
|
type=str,
|
||||||
|
default=[],
|
||||||
|
nargs="+",
|
||||||
|
dest="tweaks",
|
||||||
|
choices=["black", "rimless", "normal", "float"],
|
||||||
|
help="Tweaks to apply to the build.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--zip",
|
||||||
|
help="Whether to bundle the theme into a zip",
|
||||||
|
type=bool,
|
||||||
|
default=False,
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--patch",
|
||||||
|
help="Whether to patch the colloid submodule",
|
||||||
|
type=bool,
|
||||||
|
default=True,
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
)
|
||||||
|
|
||||||
|
return parser.parse_args()
|
69
sources/build/context.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any, Literal, List
|
||||||
|
from catppuccin.models import Flavor, Color
|
||||||
|
from .utils import find_and_replace, Subsitution
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Tweaks:
|
||||||
|
tweaks: List[str]
|
||||||
|
|
||||||
|
def has(self, tweak: str) -> bool:
|
||||||
|
return tweak in self.tweaks
|
||||||
|
|
||||||
|
def id(self) -> str:
|
||||||
|
return ",".join(self.tweaks)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Suffix:
|
||||||
|
true_value: str
|
||||||
|
test: Any
|
||||||
|
false_value: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BuildContext:
|
||||||
|
# The src dir of the Colloid copy to operate on
|
||||||
|
colloid_src_dir: str
|
||||||
|
|
||||||
|
# The root of the project
|
||||||
|
git_root: str
|
||||||
|
|
||||||
|
# The root of the output dir (as specified by --dest if given)
|
||||||
|
output_root: str
|
||||||
|
|
||||||
|
output_format: Literal["zip"] | Literal["dir"]
|
||||||
|
|
||||||
|
theme_name: str
|
||||||
|
flavor: Flavor
|
||||||
|
accent: Color
|
||||||
|
size: Literal["standard"] | Literal["compact"]
|
||||||
|
tweaks: Tweaks
|
||||||
|
|
||||||
|
def output_dir(self) -> str:
|
||||||
|
return f"{self.output_root}/{self.build_id()}"
|
||||||
|
|
||||||
|
def build_id(self) -> str:
|
||||||
|
return f"{self.theme_name}-{self.flavor.identifier}-{self.accent.identifier}-{self.size}+{self.tweaks.id() or 'default'}"
|
||||||
|
|
||||||
|
def apply_suffix(self, suffix: Suffix) -> str:
|
||||||
|
if suffix.test(self):
|
||||||
|
return suffix.true_value
|
||||||
|
else:
|
||||||
|
return suffix.false_value
|
||||||
|
|
||||||
|
def apply_tweak(self, key, default, value):
|
||||||
|
find_and_replace(
|
||||||
|
f"{self.colloid_src_dir}/sass/_tweaks-temp.scss",
|
||||||
|
Subsitution(find=f"\\${key}: {default}", replace=f"${key}: {value}"),
|
||||||
|
)
|
||||||
|
|
||||||
|
IS_DARK = Suffix(true_value="-Dark", test=lambda ctx: ctx.flavor.dark)
|
||||||
|
IS_LIGHT = Suffix(true_value="-Light", test=lambda ctx: not ctx.flavor.dark)
|
||||||
|
IS_WINDOW_NORMAL = Suffix(
|
||||||
|
true_value="-Normal", test=lambda ctx: ctx.tweaks.has("normal")
|
||||||
|
)
|
||||||
|
DARK_LIGHT = Suffix(
|
||||||
|
true_value="-Dark", false_value="-Light", test=lambda ctx: ctx.flavor.dark
|
||||||
|
)
|
9
sources/build/logger.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger("catppuccin-gtk")
|
||||||
|
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
ch = logging.StreamHandler()
|
||||||
|
formatter = logging.Formatter("[%(name)s] [%(levelname)s] - %(message)s")
|
||||||
|
ch.setFormatter(formatter)
|
||||||
|
logger.addHandler(ch)
|
35
sources/build/patches.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from .logger import logger
|
||||||
|
|
||||||
|
|
||||||
|
def apply_colloid_patches(colloid_dir, patch_dir):
|
||||||
|
colloid_dir = Path(colloid_dir).relative_to(os.getcwd())
|
||||||
|
if os.path.isfile(colloid_dir / ".patched"):
|
||||||
|
logger.info(
|
||||||
|
f'Patches seem to be applied, remove "{colloid_dir}/.patched" to force application (this may fail)'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info("Applying patches...")
|
||||||
|
# Change into colloid
|
||||||
|
for patch in [
|
||||||
|
"plank-dark.patch",
|
||||||
|
"plank-light.patch",
|
||||||
|
"sass-palette-frappe.patch",
|
||||||
|
"sass-palette-mocha.patch",
|
||||||
|
"sass-palette-latte.patch",
|
||||||
|
"sass-palette-macchiato.patch",
|
||||||
|
"theme-func.patch",
|
||||||
|
]:
|
||||||
|
path = (Path(patch_dir) / patch).relative_to(os.getcwd())
|
||||||
|
logger.info(f"Applying patch '{patch}', located at '{path}'")
|
||||||
|
subprocess.check_call(
|
||||||
|
["git", "apply", str(path), "--directory", str(colloid_dir)]
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(colloid_dir / ".patched", "w") as f:
|
||||||
|
f.write("true")
|
||||||
|
|
||||||
|
logger.info("Patching finished.")
|
390
sources/build/theme.py
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import glob
|
||||||
|
import zipfile
|
||||||
|
from .logger import logger
|
||||||
|
from .utils import find_and_replace, Subsitution
|
||||||
|
from .context import BuildContext, IS_DARK, IS_LIGHT, IS_WINDOW_NORMAL, DARK_LIGHT
|
||||||
|
|
||||||
|
|
||||||
|
def apply_tweaks(ctx: BuildContext):
|
||||||
|
ctx.apply_tweak("theme", "'default'", f"'{ctx.accent.identifier}'")
|
||||||
|
|
||||||
|
if ctx.size == "compact":
|
||||||
|
ctx.apply_tweak("compact", "'false'", "'true'")
|
||||||
|
|
||||||
|
find_and_replace(
|
||||||
|
f"{ctx.colloid_src_dir}/sass/_tweaks-temp.scss",
|
||||||
|
Subsitution(
|
||||||
|
find="@import 'color-palette-default';",
|
||||||
|
replace=f"@import 'color-palette-catppuccin-{ctx.flavor.identifier}';",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ctx.apply_tweak("colorscheme", "'default'", "'catppuccin'")
|
||||||
|
|
||||||
|
if ctx.tweaks.has("black"):
|
||||||
|
ctx.apply_tweak("blackness", "'false'", "'true'")
|
||||||
|
|
||||||
|
if ctx.tweaks.has("rimless"):
|
||||||
|
ctx.apply_tweak("rimless", "'false'", "'true'")
|
||||||
|
|
||||||
|
if ctx.tweaks.has("normal"):
|
||||||
|
ctx.apply_tweak("window_button", "'mac'", "'normal'")
|
||||||
|
|
||||||
|
if ctx.tweaks.has("float"):
|
||||||
|
ctx.apply_tweak("float", "'false'", "'true'")
|
||||||
|
|
||||||
|
|
||||||
|
SASSC_OPT = ["-M", "-t", "expanded"]
|
||||||
|
|
||||||
|
|
||||||
|
def compile_sass(src: str, dest: str) -> subprocess.Popen:
|
||||||
|
return subprocess.Popen(["sassc", *SASSC_OPT, src, dest])
|
||||||
|
|
||||||
|
|
||||||
|
def execute_build(ctx: BuildContext):
|
||||||
|
src_dir = ctx.colloid_src_dir
|
||||||
|
output_dir = ctx.output_dir()
|
||||||
|
|
||||||
|
logger.info(f"Building into '{output_dir}'...")
|
||||||
|
|
||||||
|
apply_tweaks(ctx)
|
||||||
|
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
with open(f"{output_dir}/index.theme", "w") as file:
|
||||||
|
file.write("[Desktop Entry]\n")
|
||||||
|
file.write("Type=X-GNOME-Metatheme\n")
|
||||||
|
file.write(f"Name={ctx.build_id()}\n")
|
||||||
|
file.write("Comment=An Flat Gtk+ theme based on Elegant Design\n")
|
||||||
|
file.write("Encoding=UTF-8\n")
|
||||||
|
file.write("\n")
|
||||||
|
file.write("[X-GNOME-Metatheme]\n")
|
||||||
|
file.write(f"GtkTheme={ctx.build_id()}\n")
|
||||||
|
file.write(f"MetacityTheme={ctx.build_id()}\n")
|
||||||
|
file.write(f"IconTheme=Tela-circle{ctx.apply_suffix(IS_DARK)}\n")
|
||||||
|
file.write(f"CursorTheme={ctx.flavor.name}-cursors\n")
|
||||||
|
file.write("ButtonLayout=close,minimize,maximize:menu\n")
|
||||||
|
|
||||||
|
sassc_tasks = []
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}/gnome-shell", exist_ok=True)
|
||||||
|
shutil.copyfile(
|
||||||
|
f"{src_dir}/main/gnome-shell/pad-osd.css",
|
||||||
|
f"{output_dir}/gnome-shell/pad-osd.css",
|
||||||
|
)
|
||||||
|
|
||||||
|
sassc_tasks.append(
|
||||||
|
compile_sass(
|
||||||
|
f"{src_dir}/main/gnome-shell/gnome-shell{ctx.apply_suffix(DARK_LIGHT)}.scss",
|
||||||
|
f"{output_dir}/gnome-shell/gnome-shell.css",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}/gtk-3.0", exist_ok=True)
|
||||||
|
sassc_tasks.append(
|
||||||
|
compile_sass(
|
||||||
|
f"{src_dir}/main/gtk-3.0/gtk{ctx.apply_suffix(DARK_LIGHT)}.scss",
|
||||||
|
f"{output_dir}/gtk-3.0/gtk.css",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sassc_tasks.append(
|
||||||
|
compile_sass(
|
||||||
|
f"{src_dir}/main/gtk-3.0/gtk-Dark.scss",
|
||||||
|
f"{output_dir}/gtk-3.0/gtk-dark.css",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}/gtk-4.0", exist_ok=True)
|
||||||
|
sassc_tasks.append(
|
||||||
|
compile_sass(
|
||||||
|
f"{src_dir}/main/gtk-4.0/gtk{ctx.apply_suffix(DARK_LIGHT)}.scss",
|
||||||
|
f"{output_dir}/gtk-4.0/gtk.css",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sassc_tasks.append(
|
||||||
|
compile_sass(
|
||||||
|
f"{src_dir}/main/gtk-4.0/gtk-Dark.scss",
|
||||||
|
f"{output_dir}/gtk-4.0/gtk-dark.css",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}/cinnamon", exist_ok=True)
|
||||||
|
sassc_tasks.append(
|
||||||
|
compile_sass(
|
||||||
|
f"{src_dir}/main/cinnamon/cinnamon{ctx.apply_suffix(DARK_LIGHT)}.scss",
|
||||||
|
f"{output_dir}/cinnamon/cinnamon.css",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for task in sassc_tasks:
|
||||||
|
task.wait()
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}/metacity-1", exist_ok=True)
|
||||||
|
shutil.copyfile(
|
||||||
|
f"{src_dir}/main/metacity-1/metacity-theme-3{ctx.apply_suffix(IS_WINDOW_NORMAL)}.xml",
|
||||||
|
f"{output_dir}/metacity-1/metacity-theme-3.xml",
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}/xfwm4", exist_ok=True)
|
||||||
|
shutil.copyfile(
|
||||||
|
f"{src_dir}/main/xfwm4/themerc{ctx.apply_suffix(IS_LIGHT)}",
|
||||||
|
f"{output_dir}/xfwm4/themerc",
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}-hdpi/xfwm4", exist_ok=True)
|
||||||
|
shutil.copyfile(
|
||||||
|
f"{src_dir}/main/xfwm4/themerc{ctx.apply_suffix(IS_LIGHT)}",
|
||||||
|
f"{output_dir}-hdpi/xfwm4/themerc",
|
||||||
|
)
|
||||||
|
find_and_replace(
|
||||||
|
f"{output_dir}-hdpi/xfwm4/themerc",
|
||||||
|
Subsitution(find="button_offset=6", replace="button_offset=9"),
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}-xhdpi/xfwm4", exist_ok=True)
|
||||||
|
shutil.copyfile(
|
||||||
|
f"{src_dir}/main/xfwm4/themerc{ctx.apply_suffix(IS_LIGHT)}",
|
||||||
|
f"{output_dir}-xhdpi/xfwm4/themerc",
|
||||||
|
)
|
||||||
|
|
||||||
|
find_and_replace(
|
||||||
|
f"{output_dir}-xhdpi/xfwm4/themerc",
|
||||||
|
Subsitution(find="button_offset=6", replace="button_offset=12"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if not ctx.flavor.dark:
|
||||||
|
shutil.copytree(
|
||||||
|
f"{src_dir}/main/plank/theme-Light-Catppuccin/",
|
||||||
|
f"{output_dir}/plank",
|
||||||
|
dirs_exist_ok=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
shutil.copytree(
|
||||||
|
f"{src_dir}/main/plank/theme-Dark-Catppuccin/",
|
||||||
|
f"{output_dir}/plank",
|
||||||
|
dirs_exist_ok=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_assets(ctx: BuildContext):
|
||||||
|
output_dir = ctx.output_dir()
|
||||||
|
src_dir = ctx.colloid_src_dir
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}/cinnamon/assets", exist_ok=True)
|
||||||
|
for file in glob.glob(f"{src_dir}/assets/cinnamon/theme/*.svg"):
|
||||||
|
shutil.copy(file, f"{output_dir}/cinnamon/assets")
|
||||||
|
shutil.copy(
|
||||||
|
f"{src_dir}/assets/cinnamon/thumbnail{ctx.apply_suffix(DARK_LIGHT)}.svg",
|
||||||
|
f"{output_dir}/cinnamon/thumbnail.png",
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(f"{output_dir}/gnome-shell/assets", exist_ok=True)
|
||||||
|
for file in glob.glob(f"{src_dir}/assets/gnome-shell/theme/*.svg"):
|
||||||
|
shutil.copy(file, f"{output_dir}/gnome-shell/assets")
|
||||||
|
|
||||||
|
shutil.copytree(
|
||||||
|
f"{src_dir}/assets/gtk/assets",
|
||||||
|
f"{output_dir}/gtk-3.0/assets",
|
||||||
|
dirs_exist_ok=True,
|
||||||
|
)
|
||||||
|
shutil.copytree(
|
||||||
|
f"{src_dir}/assets/gtk/assets",
|
||||||
|
f"{output_dir}/gtk-4.0/assets",
|
||||||
|
dirs_exist_ok=True,
|
||||||
|
)
|
||||||
|
shutil.copyfile(
|
||||||
|
f"{src_dir}/assets/gtk/thumbnail{ctx.apply_suffix(IS_DARK)}.svg",
|
||||||
|
f"{output_dir}/gtk-3.0/thumbnail.png",
|
||||||
|
)
|
||||||
|
shutil.copyfile(
|
||||||
|
f"{src_dir}/assets/gtk/thumbnail{ctx.apply_suffix(IS_DARK)}.svg",
|
||||||
|
f"{output_dir}/gtk-4.0/thumbnail.png",
|
||||||
|
)
|
||||||
|
|
||||||
|
theme_color = ctx.accent.hex
|
||||||
|
|
||||||
|
palette = ctx.flavor.colors
|
||||||
|
background = palette.base.hex
|
||||||
|
background_alt = palette.mantle.hex
|
||||||
|
titlebar = palette.overlay0.hex
|
||||||
|
|
||||||
|
for file in glob.glob(f"{output_dir}/cinnamon/assets/*.svg"):
|
||||||
|
find_and_replace(
|
||||||
|
file,
|
||||||
|
Subsitution(find="#5b9bf8", replace=theme_color),
|
||||||
|
Subsitution(find="#3c84f7", replace=theme_color),
|
||||||
|
)
|
||||||
|
|
||||||
|
for file in glob.glob(f"{output_dir}/gnome-shell/assets/*.svg"):
|
||||||
|
find_and_replace(
|
||||||
|
file,
|
||||||
|
Subsitution(find="#5b9bf8", replace=theme_color),
|
||||||
|
Subsitution(find="#3c84f7", replace=theme_color),
|
||||||
|
)
|
||||||
|
|
||||||
|
for file in glob.glob(f"{output_dir}/gtk-3.0/assets/*.svg"):
|
||||||
|
find_and_replace(
|
||||||
|
file,
|
||||||
|
Subsitution(find="#5b9bf8", replace=theme_color),
|
||||||
|
Subsitution(find="#3c84f7", replace=theme_color),
|
||||||
|
Subsitution(find="#ffffff", replace=background),
|
||||||
|
Subsitution(find="#2c2c2c", replace=background),
|
||||||
|
Subsitution(find="#3c3c3c", replace=background_alt),
|
||||||
|
)
|
||||||
|
|
||||||
|
for file in glob.glob(f"{output_dir}/gtk-4.0/assets/*.svg"):
|
||||||
|
find_and_replace(
|
||||||
|
file,
|
||||||
|
Subsitution(find="#5b9bf8", replace=theme_color),
|
||||||
|
Subsitution(find="#3c84f7", replace=theme_color),
|
||||||
|
Subsitution(find="#ffffff", replace=background),
|
||||||
|
Subsitution(find="#2c2c2c", replace=background),
|
||||||
|
Subsitution(find="#3c3c3c", replace=background_alt),
|
||||||
|
)
|
||||||
|
|
||||||
|
if ctx.flavor.dark:
|
||||||
|
find_and_replace(
|
||||||
|
f"{output_dir}/cinnamon/thumbnail.png",
|
||||||
|
Subsitution(find="#2c2c2c", replace=background),
|
||||||
|
Subsitution(find="#5b9bf8", replace=theme_color),
|
||||||
|
)
|
||||||
|
|
||||||
|
find_and_replace(
|
||||||
|
f"{output_dir}/gtk-3.0/thumbnail.png",
|
||||||
|
Subsitution(find="#5b9bf8", replace=theme_color),
|
||||||
|
Subsitution(find="#2c2c2c", replace=background),
|
||||||
|
)
|
||||||
|
|
||||||
|
find_and_replace(
|
||||||
|
f"{output_dir}/gtk-4.0/thumbnail.png",
|
||||||
|
Subsitution(find="#5b9bf8", replace=theme_color),
|
||||||
|
Subsitution(find="#2c2c2c", replace=background),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
find_and_replace(
|
||||||
|
f"{output_dir}/cinnamon/thumbnail.png",
|
||||||
|
Subsitution(find="#ffffff", replace=background),
|
||||||
|
Subsitution(find="#f2f2f2", replace=titlebar),
|
||||||
|
Subsitution(find="#3c84f7", replace=theme_color),
|
||||||
|
)
|
||||||
|
|
||||||
|
find_and_replace(
|
||||||
|
f"{output_dir}/gtk-3.0/thumbnail.png",
|
||||||
|
Subsitution(find="#f2f2f2", replace=titlebar),
|
||||||
|
Subsitution(find="#3c84f7", replace=theme_color),
|
||||||
|
)
|
||||||
|
|
||||||
|
find_and_replace(
|
||||||
|
f"{output_dir}/gtk-4.0/thumbnail.png",
|
||||||
|
Subsitution(find="#f2f2f2", replace=titlebar),
|
||||||
|
Subsitution(find="#3c84f7", replace=theme_color),
|
||||||
|
)
|
||||||
|
|
||||||
|
for file in glob.glob(f"{src_dir}/assets/cinnamon/common-assets/*.svg"):
|
||||||
|
shutil.copy(file, f"{output_dir}/cinnamon/assets")
|
||||||
|
|
||||||
|
for file in glob.glob(
|
||||||
|
f"{src_dir}/assets/cinnamon/assets{ctx.apply_suffix(IS_DARK)}/*.svg"
|
||||||
|
):
|
||||||
|
shutil.copy(file, f"{output_dir}/cinnamon/assets")
|
||||||
|
|
||||||
|
for file in glob.glob(f"{src_dir}/assets/gnome-shell/common-assets/*.svg"):
|
||||||
|
shutil.copy(file, f"{output_dir}/gnome-shell/assets")
|
||||||
|
|
||||||
|
for file in glob.glob(
|
||||||
|
f"{src_dir}/assets/gnome-shell/assets{ctx.apply_suffix(IS_DARK)}/*.svg"
|
||||||
|
):
|
||||||
|
shutil.copy(file, f"{output_dir}/gnome-shell/assets")
|
||||||
|
|
||||||
|
for file in glob.glob(f"{src_dir}/assets/gtk/symbolics/*.svg"):
|
||||||
|
shutil.copy(file, f"{output_dir}/gtk-3.0/assets")
|
||||||
|
shutil.copy(file, f"{output_dir}/gtk-4.0/assets")
|
||||||
|
|
||||||
|
for file in glob.glob(
|
||||||
|
f"{src_dir}/assets/metacity-1/assets{ctx.apply_suffix(IS_WINDOW_NORMAL)}/*.svg"
|
||||||
|
):
|
||||||
|
shutil.copy(file, f"{output_dir}/metacity-1/assets")
|
||||||
|
shutil.copy(
|
||||||
|
f"{src_dir}/assets/metacity-1/thumbnail{ctx.apply_suffix(IS_DARK)}.png",
|
||||||
|
f"{output_dir}/metacity-1/thumbnail.png",
|
||||||
|
)
|
||||||
|
|
||||||
|
xfwm4_assets = f"{ctx.git_root}/patches/xfwm4/generated/assets-catppuccin-{ctx.flavor.identifier}"
|
||||||
|
for file in glob.glob(xfwm4_assets + "/*"):
|
||||||
|
shutil.copy(file, f"{output_dir}/xfwm4")
|
||||||
|
|
||||||
|
xfwm4_assets = xfwm4_assets + "-hdpi/*"
|
||||||
|
for file in glob.glob(xfwm4_assets):
|
||||||
|
shutil.copy(file, f"{output_dir}-hdpi/xfwm4")
|
||||||
|
|
||||||
|
xfwm4_assets = xfwm4_assets + "-xhdpi/*"
|
||||||
|
for file in glob.glob(xfwm4_assets):
|
||||||
|
shutil.copy(file, f"{output_dir}-xhdpi/xfwm4")
|
||||||
|
|
||||||
|
|
||||||
|
def zip_dir(path, zip_file):
|
||||||
|
for root, _, files in os.walk(path):
|
||||||
|
for file in files:
|
||||||
|
zip_file.write(
|
||||||
|
os.path.join(root, file),
|
||||||
|
os.path.relpath(os.path.join(root, file), os.path.join(path, "..")),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def zip_artifacts(dir_list, zip_name, remove=True):
|
||||||
|
with zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED) as zipf:
|
||||||
|
for dir in dir_list:
|
||||||
|
zip_dir(dir, zipf)
|
||||||
|
|
||||||
|
if remove:
|
||||||
|
for dir in dir_list:
|
||||||
|
shutil.rmtree(dir)
|
||||||
|
|
||||||
|
|
||||||
|
def build_with_context(ctx: BuildContext):
|
||||||
|
build_info = f"""Build info:
|
||||||
|
build_root: {ctx.output_root}
|
||||||
|
src_root: {ctx.colloid_src_dir}
|
||||||
|
theme_name: {ctx.theme_name}
|
||||||
|
flavor: {ctx.flavor.identifier}
|
||||||
|
accent: {ctx.accent.identifier}
|
||||||
|
size: {ctx.size}
|
||||||
|
tweaks: {ctx.tweaks}"""
|
||||||
|
logger.info(build_info)
|
||||||
|
|
||||||
|
execute_build(ctx)
|
||||||
|
logger.info("Main build complete")
|
||||||
|
|
||||||
|
logger.info("Bundling assets...")
|
||||||
|
make_assets(ctx)
|
||||||
|
logger.info("Asset bundling done")
|
||||||
|
|
||||||
|
if ctx.output_format == "zip":
|
||||||
|
zip_artifacts(
|
||||||
|
[
|
||||||
|
ctx.output_dir(),
|
||||||
|
f"{ctx.output_dir()}-hdpi",
|
||||||
|
f"{ctx.output_dir()}-xhdpi",
|
||||||
|
],
|
||||||
|
f"{ctx.output_root}/{ctx.build_id()}.zip",
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def gnome_shell_version(src_dir):
|
||||||
|
# Hardcoded here, Colloid checks for this on end user machines
|
||||||
|
# but we cannot do that. Old build system would've resulted in this too.
|
||||||
|
gs_version = "46-0"
|
||||||
|
|
||||||
|
shutil.copyfile(
|
||||||
|
f"{src_dir}/sass/gnome-shell/_common.scss",
|
||||||
|
f"{src_dir}/sass/gnome-shell/_common-temp.scss",
|
||||||
|
)
|
||||||
|
find_and_replace(
|
||||||
|
f"{src_dir}/sass/gnome-shell/_common-temp.scss",
|
||||||
|
Subsitution(
|
||||||
|
find="@import 'widgets-40-0';",
|
||||||
|
replace=f"@import 'widgets-{gs_version}';",
|
||||||
|
),
|
||||||
|
)
|
23
sources/build/utils.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Subsitution:
|
||||||
|
find: str
|
||||||
|
replace: str
|
||||||
|
|
||||||
|
|
||||||
|
def find_and_replace(path: str, *subs: Subsitution):
|
||||||
|
with open(path, "r+") as f:
|
||||||
|
content = f.read()
|
||||||
|
f.seek(0)
|
||||||
|
f.truncate()
|
||||||
|
for sub in subs:
|
||||||
|
content = re.sub(sub.find, sub.replace, content)
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
def init_tweaks_temp(src_dir):
|
||||||
|
shutil.copyfile(f"{src_dir}/sass/_tweaks.scss", f"{src_dir}/sass/_tweaks-temp.scss")
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
@@ -2,7 +2,11 @@ from typing import List
|
|||||||
from catppuccin import PALETTE
|
from catppuccin import PALETTE
|
||||||
from catppuccin.models import Flavor
|
from catppuccin.models import Flavor
|
||||||
|
|
||||||
import re, os, shutil, subprocess, time
|
import re
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
@@ -83,9 +87,9 @@ def generate_for_flavor(flavor: Flavor):
|
|||||||
# Setup the palette
|
# Setup the palette
|
||||||
palette = flavor.colors
|
palette = flavor.colors
|
||||||
|
|
||||||
close_color=f'#{palette.red.hex}'
|
close_color = f"#{palette.red.hex}"
|
||||||
max_color=f'#{palette.green.hex}'
|
max_color = f"#{palette.green.hex}"
|
||||||
min_color=f'#{palette.yellow.hex}'
|
min_color = f"#{palette.yellow.hex}"
|
||||||
|
|
||||||
# We expand the source assets into the 4 flavors, but need to map between
|
# We expand the source assets into the 4 flavors, but need to map between
|
||||||
# Their definition of dark and ours. This means that latte will get the -light assets
|
# Their definition of dark and ours. This means that latte will get the -light assets
|
||||||
@@ -187,6 +191,7 @@ screen_to_dpi = {
|
|||||||
"-xhdpi": "192",
|
"-xhdpi": "192",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def render_for_screen(state: RenderState, flavor: Flavor, screen: str, ident: str):
|
def render_for_screen(state: RenderState, flavor: Flavor, screen: str, ident: str):
|
||||||
# NOTE: We do not generate for the -normal variant currently, that would just be
|
# NOTE: We do not generate for the -normal variant currently, that would just be
|
||||||
# a stupid amount of compute and time for little benefit
|
# a stupid amount of compute and time for little benefit
|
||||||
@@ -215,9 +220,7 @@ def render_for_screen(state: RenderState, flavor: Flavor, screen: str, ident: st
|
|||||||
|
|
||||||
|
|
||||||
def render_for_flavor(flavor: Flavor, state: RenderState):
|
def render_for_flavor(flavor: Flavor, state: RenderState):
|
||||||
print(
|
print(f"Starting render tasks for {flavor.identifier}")
|
||||||
f"Starting render tasks for {flavor.identifier}"
|
|
||||||
)
|
|
||||||
for ident in INDEX:
|
for ident in INDEX:
|
||||||
render_for_screen(state=state, flavor=flavor, screen="", ident=ident)
|
render_for_screen(state=state, flavor=flavor, screen="", ident=ident)
|
||||||
render_for_screen(state=state, flavor=flavor, screen="-hdpi", ident=ident)
|
render_for_screen(state=state, flavor=flavor, screen="-hdpi", ident=ident)
|