Compare commits

...

41 Commits

Author SHA1 Message Date
github-actions[bot]
3cb2f3ab4a chore(main): release 1.0.3 (#263)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-01 19:52:04 +01:00
iruzo
cadc3e2ac2 perf: improving performance, layout (#215)
Co-authored-by: nullishamy <git@amyerskine.me>
2024-06-01 19:49:55 +01:00
Hammy
54633c0f72 docs(README): hardcode version in more places (#256)
As we make use of release-please, I feel like it's
nicer to hardcode the versions as they will
automatically get updated upon release.
2024-05-27 23:38:55 +01:00
github-actions[bot]
4173b70b91 chore(main): release 1.0.2 (#253) 2024-05-27 19:01:50 +01:00
Hammy
006c148f10 ci(release): remove token (#252) 2024-05-27 18:58:15 +01:00
github-actions[bot]
409628ed32 chore(main): release 1.0.1 (#248)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Hammy <58985301+sgoudham@users.noreply.github.com>
2024-05-27 18:50:11 +01:00
Hammy
22a62eab04 ci(release): update name & gen patches (#250) 2024-05-27 18:46:40 +01:00
Hammy
b421b50380 ci: use token for release-please (#249) 2024-05-27 18:41:28 +01:00
nullishamy
cae57c80f8 fix: alt tab menu (#209) 2024-05-27 18:28:13 +01:00
Hammy
e7b8bb2c4a build: use release-please (#235) 2024-05-27 18:27:00 +01:00
nullishamy
e11c9b71f0 chore: update install script to v1.0.0 (#247) 2024-05-27 18:23:07 +01:00
nullishamy
09b1cd28d4 chore: update install script to rc6 (#245) 2024-05-27 15:49:56 +01:00
nullishamy
8b54c05ff5 fix: titlebars should be mantle not crust (#238) 2024-05-27 15:09:24 +01:00
nullishamy
03fd2c9b7e fix: xfwm4 assets (#240) 2024-05-27 15:09:02 +01:00
nullishamy
8b459944c4 fix: add build suffix (#241) 2024-05-27 15:04:27 +01:00
nullishamy
124944fc5b docs: architecture docs (#242) 2024-05-27 14:16:28 +01:00
nullishamy
641f692dd8 chore: update install script to rc5 (#233) 2024-05-24 21:30:16 +01:00
nullishamy
a36e8a3b91 fix: allow all accents to be used (#232) 2024-05-24 21:19:52 +01:00
nullishamy
327d02c0ec ci: exit with non zero exit code when a build fails (#231) 2024-05-24 20:04:15 +01:00
Isabel
495589d9f8 fix: patches (#229) 2024-05-24 19:54:48 +01:00
nullishamy
455dd7b403 chore: update install script to rc4 (#227) 2024-05-24 16:20:29 +01:00
Isabel
b40c355ba3 fix(patches): allow to work on windows (#226) 2024-05-24 16:14:30 +01:00
nullishamy
001ba5bf12 feat: reintroduce black tweak as OLED-esque variant (#213) 2024-05-24 16:10:06 +01:00
nullishamy
1c336f20ad chore: issue template (#222) 2024-05-24 15:36:01 +01:00
Omar
14cee4c1f5 docs: flatpak guide (#221)
Co-authored-by: Isabel <isabel@isabelroses.com>
2024-05-24 13:28:08 +01:00
nullishamy
febd32f208 docs: upstreaming procedure (#219)
* docs: upstreaming procedure

* docs: math

Co-authored-by: Isabel <isabel@isabelroses.com>

---------

Co-authored-by: Isabel <isabel@isabelroses.com>
2024-05-22 01:31:17 +01:00
nullishamy
663f6c61f8 fix: installation (#210) 2024-05-21 14:22:56 +01:00
nullishamy
b2f3d267e4 docs: add new build reqs in (#208) 2024-05-21 14:22:33 +01:00
nullishamy
e72416ae5e docs: add resources to contributing guide (#211) 2024-05-21 14:22:05 +01:00
nullishamy
87a5f92f4b chore: update install script to rc3 (#207) 2024-05-20 23:14:54 +01:00
nullishamy
b508c0ff2d feat: xfwm4 asset generation (#206) 2024-05-20 23:09:22 +01:00
nullishamy
44be6bb595 ci: build in PRs (#202)
* ci: build in PRs

* ci: improve naming

* ci: upload individual artifacts

* ci: try running when PRs are pushed to

* ci: name

* ci: try individual again

* ci: friendship ended with gha; gitlab ci/cd is my new friend

* feat: introduce --from-artifact opt in install script
2024-05-20 16:34:12 +01:00
nullishamy
6eeda71fb7 fix: disallow 'all' in install.py accent options (#205) 2024-05-20 13:50:02 +01:00
nullishamy
5c3ec52e39 fix: actually apply our accents (#199)
* fix: actually apply our accents

* chore: remove unused patches
2024-05-20 12:54:33 +01:00
nullishamy
084c3acde7 fix: use rc2 in the install script (#201) 2024-05-20 12:40:09 +01:00
Isabel
66c2314f9c fix: argument --accent behaves like the old argument (#200)
* fix: argument --accent behaves like the old argument

* fix: seprate arg for all accents

* fix: logger accurracy

* ci: call correct build arg
2024-05-20 12:33:13 +01:00
Omar
8d8949e96d docs: improve and fix README formatting / style (#197)
Co-authored-by: Willow <42willow@pm.me>
2024-05-20 12:06:42 +01:00
Isabel
89d94d64cb refactor: reduce repeated code (#198) 2024-05-20 11:56:58 +01:00
nullishamy
13f2a344c8 fix: use v1.0.0-rc1 for the install script (#192) 2024-05-20 02:10:54 +01:00
renovate[bot]
63c8fd9dee chore: Configure Renovate (#184) 2024-05-20 01:38:45 +01:00
nullishamy
0fa26adccc docs(README): overhaul with updated information (#187) 2024-05-20 01:18:48 +01:00
57 changed files with 2710 additions and 19167 deletions

View File

@@ -25,18 +25,24 @@ body:
attributes:
label: How did you install the theme?
options:
- GitHub Release
- AUR Package
- catppuccin/nix
- Nixpkgs
- Flatpak
- From source (Python script)
- From release (install.py)
- From release (manual installation)
- From source (build.py)
- Nix (nixpkgs / catppuccin/nix)
- AUR package
validations:
required: true
- type: checkboxes
attributes:
label: Are you running the latest release, and if not does the issue persist there?
description: "Our latest release can be found under the [GitHub releases](https://github.com/catppuccin/gtk/releases/latest)"
options:
- label: I am running on the latest release & the issue persists there
required: true
- type: checkboxes
attributes:
label: If using GTK4, have you symlinked the "gtk-4.0" folder?
description: "The instructions for symlinking are described in the [README](https://github.com/catppuccin/gtk/tree/877e75568c9bb0d57c7ddda85b246fa17af45e57?tab=readme-ov-file#for-gtk-4-users)."
description: "The instructions for symlinking are described at the bottom of the [Manual Installation guide](https://github.com/catppuccin/gtk/tree/663f6c61f8cf1fcbbeb72110bf6b0a0214755245?tab=readme-ov-file#manual-installation)."
options:
- label: I have symlinked the `gtk-4.0` folder.
required: false

66
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
name: "Generate test artifacts"
on:
pull_request:
types: [opened, reopened, synchronize]
# env:
# TAG: latest
jobs:
build:
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:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"
- name: Install dependencies
run: |
sudo apt update && sudo apt install -y sassc inkscape optipng
pip install -r requirements.txt
- name: Generate themes
run: |
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
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: '${{ github.sha }}-artifacts'
path: ./releases/*.zip

27
.github/workflows/docker.yml vendored Normal file
View 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 }}"

View File

@@ -1,33 +1,68 @@
name: "Release"
name: release
on:
workflow_dispatch:
push:
tags:
- "v*"
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
id: release
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
release:
runs-on: ubuntu-latest
needs: release-please
if: ${{ needs.release-please.outputs.release_created }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"
- name: Install dependencies
run: pip install -r requirements.txt
- name: Install colloid specific dependencies
run: sudo apt update && sudo apt install -y sassc inkscape optipng
- name: Generate themes
run: |
python ./build.py mocha -a all --zip -d $PWD/releases &&
python ./build.py macchiato -a all --zip -d $PWD/releases &&
python ./build.py frappe -a all --zip -d $PWD/releases &&
python ./build.py latte -a all --zip -d $PWD/releases
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
- name: Add zips to release
uses: softprops/action-gh-release@v2
with:
draft: true
files: ./releases/*.zip
run: gh release upload ${{ needs.release-please.outputs.tag_name }} releases/*.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3
.gitignore vendored
View File

@@ -8,6 +8,9 @@ lib*/
*.cfg
.direnv
build/
*.log
.ruff-cache
.tmp
# Releases folder
releases

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "colloid"]
path = colloid
path = sources/colloid
url = https://github.com/vinceliuice/Colloid-gtk-theme.git

View File

@@ -0,0 +1,3 @@
{
".": "1.0.3"
}

8
CHANGELOG.md Normal file
View File

@@ -0,0 +1,8 @@
# Changelog
## [1.0.3](https://github.com/catppuccin/gtk/compare/v1.0.2...v1.0.3) (2024-06-01)
### Performance Improvements
* improving performance, layout ([#215](https://github.com/catppuccin/gtk/issues/215)) ([cadc3e2](https://github.com/catppuccin/gtk/commit/cadc3e2ac24f93c6641f9f87ee5d044433c65ce7))

187
README.md
View File

@@ -1,14 +1,14 @@
<h3 align="center">
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/logos/exports/1544x1544_circle.png" width="100" alt="Logo"/><br/>
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/misc/transparent.png" height="30" width="0px"/>
Catppuccin for <a href="https://gtk.org/">GTK</a>
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/misc/transparent.png" height="30" width="0px"/>
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/logos/exports/1544x1544_circle.png" width="100" alt="Logo"/><br/>
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/misc/transparent.png" height="30" width="0px"/>
Catppuccin for <a href="https://gtk.org/">GTK</a>
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/misc/transparent.png" height="30" width="0px"/>
</h3>
<p align="center">
<a href="https://github.com/catppuccin/gtk/stargazers"><img src="https://img.shields.io/github/stars/catppuccin/gtk?colorA=363a4f&colorB=b7bdf8&style=for-the-badge"></a>
<a href="https://github.com/catppuccin/gtk/issues"><img src="https://img.shields.io/github/issues/catppuccin/gtk?colorA=363a4f&colorB=f5a97f&style=for-the-badge"></a>
<a href="https://github.com/catppuccin/gtk/contributors"><img src="https://img.shields.io/github/contributors/catppuccin/gtk?colorA=363a4f&colorB=a6da95&style=for-the-badge"></a>
<a href="https://github.com/catppuccin/gtk/stargazers"><img src="https://img.shields.io/github/stars/catppuccin/gtk?colorA=363a4f&colorB=b7bdf8&style=for-the-badge"></a>
<a href="https://github.com/catppuccin/gtk/issues"><img src="https://img.shields.io/github/issues/catppuccin/gtk?colorA=363a4f&colorB=f5a97f&style=for-the-badge"></a>
<a href="https://github.com/catppuccin/gtk/contributors"><img src="https://img.shields.io/github/contributors/catppuccin/gtk?colorA=363a4f&colorB=a6da95&style=for-the-badge"></a>
</p>
<p align="center">
@@ -17,78 +17,56 @@
This GTK theme is based on the [Colloid](https://github.com/vinceliuice/Colloid-gtk-theme) theme made by [vinceliuice](https://github.com/vinceliuice)
## Usage
## Installation
### Requirements
This GTK theme requires:
- GTK >=3.20
- `python3`
- `gnome-themes-extra` (or `gnome-themes-standard`)
- GTK `>=3.20`
- Python 3+
To build the theme, make sure the following packages are installed:
- `sassc`
- `inkscape`
- `optipng`
### Automated script
### Manually
We provide a Python script to automate the process of installing the theme:
<!-- x-release-please-start-version -->
1. Download and extract the theme zip from the [latest release](https://github.com/catppuccin/gtk/releases/latest/).
2. Move the theme folder to the `~/.local/share/themes` directory.
3. Select the downloaded theme via your desktop specific tweaks application (GNOME Tweaks on GNOME 3+).
4. To theme other apps that are using GTK, make sure to run the following command:
```bash
export THEME_DIR="~/.local/share/themes/catppuccin-<flavor>-<accent>-standard+default"
mkdir -p "${HOME}/.config/gtk-4.0" &&
ln -sf "${THEME_DIR}/gtk-4.0/assets" "${HOME}/.config/gtk-4.0/assets" &&
ln -sf "${THEME_DIR}/gtk-4.0/gtk.css" "${HOME}/.config/gtk-4.0/gtk.css" &&
ln -sf "${THEME_DIR}/gtk-4.0/gtk-dark.css" "${HOME}/.config/gtk-4.0/gtk-dark.css"
```
### Flatpak
In order for Flatpak to access the theme, make sure to run the following command:
```bash
sudo flatpak override --filesystem=$HOME/.local/share/themes
```
Then, run the following command to apply the theme.
```bash
export THEME_DIR="~/.local/share/themes/catppuccin-<flavor>-<accent>-standard+default"
sudo flatpak override --env=GTK_THEME=$THEME_DIR
```
### Using the install script to install the theme
To install the theme using the install script, run `install.py`:
```
curl -LsSO "https://raw.githubusercontent.com/catppuccin/gtk/v1.0.3/install.py"
python3 install.py <flavor> <accent>
[catppuccin-gtk] [INFO] - Installation info:
flavor: mocha
accent: blue
dest: /home/<user>/.local/share/themes
link: False
remote_url: https://github.com/catppuccin/gtk/releases/download/v1.0.3/catppuccin-mocha-blue-standard+default.zip
[catppuccin-gtk] [INFO] - Starting download...
[catppuccin-gtk] [INFO] - Response status: 200
[catppuccin-gtk] [INFO] - Download finished, zip is valid
[catppuccin-gtk] [INFO] - Verifying download..
[catppuccin-gtk] [INFO] - Download verified
[catppuccin-gtk] [INFO] - Extracting...
[catppuccin-gtk] [INFO] - Extraction complete
[catppuccin-gtk] [INFO] - Theme installation complete!
```
If you have adwaita installed, make sure to include `--link` in order to add symlinks for it:
```
python3 install.py <flavor> <accent> --link
```
Run the command and the gtk theme should be installed!
### Arch Linux
### AUR
We have 4 AUR packages for all the 4 flavours (Latte, Frappe, Macchiato, Mocha)
With your favourite AUR helper, you can install one of these flavors:
With your favourite AUR helper, you can install your flavor of choice:
```bash
yay -S catppuccin-gtk-theme-<flavor>
paru -S catppuccin-gtk-theme-<flavor>
```
### Nix
We suggest you use [catppuccin/nix](https://github.com/catppuccin/nix).
Alternatively, you can use [catppuccin-gtk](https://github.com/NixOS/nixpkgs/blob/master/pkgs/data/themes/catppuccin-gtk/default.nix) from nixpkgs.
We have created a Nix module ([catppuccin/nix](https://github.com/catppuccin/nix)) for theming apps under Nix, and recommend that you use it.
You can set up our Nix module for GTK with the following config:
```nix
{inputs, ...}: {
imports = [inputs.catppuccin.homeManagerModules.catppuccin];
gtk = {
enable = true;
catppuccin = {
@@ -103,19 +81,102 @@ Alternatively, you can use [catppuccin-gtk](https://github.com/NixOS/nixpkgs/blo
```
> [!TIP]
> For further information on the options available, see the [full documentation](https://github.com/catppuccin/nix/blob/main/docs/home-manager-options.md#gtkcatppuccinenable).
> For further information on the options available with our module, see the [full documentation](https://github.com/catppuccin/nix/blob/main/docs/home-manager-options.md#gtkcatppuccinenable).
### For Other Distros
Alternatively, if you are not using our Nix module, you can grab the theme from [nixpkgs/catppuccin-gtk](https://github.com/NixOS/nixpkgs/blob/master/pkgs/data/themes/catppuccin-gtk/default.nix)
Refer to [Using the install script to install the theme](https://github.com/catppuccin/gtk/edit/refactor/build-system/README.md#installing-the-theme-manually) or [Installing the theme manually](https://github.com/catppuccin/gtk/edit/refactor/build-system/README.md#installing-the-theme-manually).
## Flatpak
Flatpak by default restricts access to themes, to allow access, use the following:
```bash
sudo flatpak override --filesystem=$HOME/.local/share/themes
```
### Theming the GDM Theme
In order to theme the GDM theme, install the `gdm-settings` app, select the Catppuccin theme, and click *Save*.
After you've allowed access, set the theme, using the following:
```bash
# Change to suite your flavor / accent combination
export FLAVOR="mocha"
export ACCENT="mauve"
# Set the theme
sudo flatpak override --env=GTK_THEME="catppuccin-${FLAVOR}-${ACCENT}-standard+default"
```
### Manual installation
If your distro does not package our theme, and the installation script will not work for your use case, you can pull down releases and extract them yourself. You can find the [latest release on GitHub](https://github.com/catppuccin/gtk/releases/tag/v1.0.3).
```bash
cd ~/.local/share/themes
# Set the root URL
export ROOT_URL="https://https://github.com/catppuccin/gtk/releases/download"
# Change to the tag you want to download
export RELEASE="v1.0.3"
# Change to suite your flavor / accent combination
export FLAVOR="mocha"
export ACCENT="mauve"
curl -LsS "${ROOT_URL}/${RELEASE}/catppuccin-${FLAVOR}-${ACCENT}-standard+default.zip"
# Extract the catppuccin zip file
unzip catppuccin-${FLAVOR}-${ACCENT}-standard+default.zip
# Set the catppuccin theme directory
export THEME_DIR="$HOME/.local/share/themes/catppuccin-${FLAVOR}-${ACCENT}-standard+default"
# Optionally, add support for libadwaita
mkdir -p "${HOME}/.config/gtk-4.0" &&
ln -sf "${THEME_DIR}/gtk-4.0/assets" "${HOME}/.config/gtk-4.0/assets" &&
ln -sf "${THEME_DIR}/gtk-4.0/gtk.css" "${HOME}/.config/gtk-4.0/gtk.css" &&
ln -sf "${THEME_DIR}/gtk-4.0/gtk-dark.css" "${HOME}/.config/gtk-4.0/gtk-dark.css"
```
<!-- x-release-please-end -->
## Building
If our prebuilt offerings do not match your requirements, you will have to build the theme from source.
### Requirements
- Python 3+
- `sassc`, the Sass compiler
- `inkscape`, `optipng`, for rendering PNGs
> [!WARNING]
> We use a submodule to bring in colloid, the theme this theme is based on. You will need to clone
> with `git clone <url> --recurse-submodules` to bring in the submodule.
To build the theme, simply invoke `build.py`:
```bash
python3 build.py mocha --dest ./build -a rosewater --tweaks rimless
[catppuccin-gtk] [INFO] - Patches seem to be applied, remove "colloid/.patched" to force application (this may fail)
[catppuccin-gtk] [INFO] - Building temp tweaks file
[catppuccin-gtk] [INFO] - Inserting gnome-shell imports
[catppuccin-gtk] [INFO] - Building main theme
[catppuccin-gtk] [INFO] - Build info:
build_root: ./build
theme_name: catppuccin
flavor: mocha
accent: rosewater
size: standard
tweaks: Tweaks(tweaks=['rimless'])
[catppuccin-gtk] [INFO] - Building into './build/catppuccin-mocha-rosewater-standard+rimless'...
[catppuccin-gtk] [INFO] - Main build complete
[catppuccin-gtk] [INFO] - Bundling assets...
[catppuccin-gtk] [INFO] - Asset bundling done
[catppuccin-gtk] [INFO] - Done!
```
You can now find the built theme under `./build`. If you want to package the theme up as a zip instead, pass `--zip` to the build script.
## 💝 Thanks to
**Current maintainers**
- [nullishamy](https://github.com/nullishamy)
- [npv12](https://github.com/npv12)
- [ghostx31](https://github.com/ghostx31)
- [Syndrizzle](https://github.com/Syndrizzle)

679
build.py
View File

@@ -1,667 +1,22 @@
#! /usr/bin/env python3
import os, re, shutil, subprocess, argparse, glob, logging, zipfile
import os
import sys
import time
import os
from dataclasses import dataclass
from typing import Any, Literal, List
from sources.build import execute_build
from sources.build.args import parse_args
from sources.build.logger import logger
from catppuccin import PALETTE
from catppuccin.models import Flavor, Color
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:
return f"{self.build_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 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"
)
else:
shutil.copytree(
f"{SRC_DIR}/main/plank/theme-Dark-Catppuccin/", f"{output_dir}/plank"
)
def 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}';",
)
# Accent translation
ctp_to_colloid = {
"rosewater": "pink",
"flamingo": "pink",
"pink": "pink",
"mauve": "purple",
"red": "red",
"maroon": "red",
"peach": "orange",
"yellow": "yellow",
"green": "green",
"teal": "teal",
"sky": "teal",
"sapphire": "default",
"blue": "default",
"lavender": "default",
}
def translate_accent(ctp_accent: Color):
return ctp_to_colloid[ctp_accent.identifier]
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'", translate_accent(ctx.accent))
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",
)
# TODO: Make our own assets for this and patch them in with the patch system, then code it to be
# {src_dir}/assets/xfwm4/assets{light_suffix}-Catppuccin/
# where assets-Light-Catppuccin will have latte
# nad assets-Catppuccin will have mocha or something
for file in glob.glob(f"{SRC_DIR}/assets/xfwm4/assets{ctx.apply_suffix(IS_LIGHT)}/*.png"):
shutil.copy(file, f"{output_dir}/xfwm4")
for file in glob.glob(f"{SRC_DIR}/assets/xfwm4/assets{ctx.apply_suffix(IS_LIGHT)}-hdpi/*.png"):
shutil.copy(file, f"{output_dir}-hdpi/xfwm4")
for file in glob.glob(f"{SRC_DIR}/assets/xfwm4/assets{ctx.apply_suffix(IS_LIGHT)}-xhdpi/*.png"):
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",
"sass-colors.patch",
"sass-palette-frappe.patch",
"sass-palette-mocha.patch",
"sass-palette-latte.patch",
"sass-palette-macchiato.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",
dest="accent",
choices=[
"rosewater",
"flamingo",
"pink",
"mauve",
"red",
"maroon",
"peach",
"yellow",
"green",
"teal",
"sky",
"sapphire",
"blue",
"lavender",
"all",
],
help="Accent of the theme.",
)
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():
if __name__ == "__main__":
git_root = os.path.dirname(os.path.realpath(__file__))
args = parse_args()
if args.patch:
apply_colloid_patches()
palette = getattr(PALETTE, args.flavor)
accents = [
"rosewater",
"flamingo",
"pink",
"mauve",
"red",
"maroon",
"peach",
"yellow",
"green",
"teal",
"sky",
"sapphire",
"blue",
"lavender",
]
try:
start = time.time()
execute_build(git_root, args)
end = time.time() - start
if args.accent == "all":
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,
)
tweaks_temp()
gnome_shell_version()
build_theme(ctx)
logger.info("Done!")
else:
accent = getattr(palette.colors, args.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")
tweaks_temp()
logger.info("Inserting gnome-shell imports")
gnome_shell_version()
logger.info("Building main theme")
build_theme(ctx)
logger.info("Done!")
try:
main()
except Exception as e:
logger.error("Something went wrong when building the theme:", exc_info=e)
logger.info("")
logger.info(f"Built in {round(end, 3)}s")
except Exception as e:
logger.error("Something went wrong when building the theme:", exc_info=e)

14
docker/Dockerfile Normal file
View 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
View 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
View 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

74
docs/ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,74 @@
## Build pipeline
The GTK port has a fairly complicated build pipeline / system, chiefly stemming from our use of Colloid as a base theme.
We use Colloid as a base to reduce development overhead of creating our own theme from scratch, we look to replace this in the future
to give us more flexibility and control over the theme (see https://github.com/catppuccin/gtk/issues/164).
We have reimplemented Colloid's build system (previously implemented in Shell) in Python to make it easier to maintain, extend, and iterate on.
With this re-implementation, we have several distinct components in the system, described below:
1) Patching
2) SCSS
2) Assets
## Patching
We patch our colloid submodule to add additional functionality and (temporarily) fix bugs found in Colloid.
We do this through `.patch` files, applied with `git apply` when the build script boots up.
The build script will store some state in the submodule to ensure it does not get needlessly patched.
The patches are stored in `patches/colloid`, and currently have our palette, the Plank theme, and a modification to Colloid
to allow all of our accents to load. When we find issues in Colloid, they will be patched through this system before being submitted upstream.
## SCSS
[This section assumes the directory root is at `colliod/src/sass`]
The bulk of the theme is implemented here, in SCSS. This is by far the most modular part of Colloid out of the box, requiring minimal patching from our end to function.
To start, we move the Colloid submodule into its own temporary copy. This is to allow us to run multiple builds in parallel, which would be otherwise impossible due to the
file changes necessitated by each build, described below.
With our temporary copy established, we generate the 'tweaks' for the build. This sets up a file (`_tweaks-temp.scss`) which describes the various knobs and dials for the build:
```scss
@import 'color-palette-catppuccin-mocha';
$colorscheme: 'catppuccin';
$colortype: 'system';
$opacity: 'default';
$theme: 'mauve';
$compact: 'false';
$translucent: 'false';
$panel_opacity: 1.0;
$blackness: 'false';
$rimless: 'false';
$window_button: 'mac';
$float: 'false';
```
We edit in the correct palette import for the flavour we're building, and set the other variables based on user / build state input.
With the tweaks setup, we can now invoke `sassc` (the SCSS compiler) to build all of our CSS files. We run all of the SCSS builds in parallel, for performance.
With the SCSS complete, we have now finished most of the work required for the build.
## Assets
We build our own assets to ship with the theme, based on the processes used in Colloid.
We build assets for GTK, to include UI elements such as buttons, checkboxes,
etc. This is done through standard find-and-replace, as these assets are just SVGs. We do not support GTK2, so do not have to support the older PNG assets used there.
We also build assets for Xfce's Xfwm4, which are first patched from a source SVG, and then rendered through the `inkscape` CLI.
This operation is done once, at the start of a build process (e.g CI, to be reused for every subsequent build), or once until they change in the future for local development.
The script to generate these assets can be found at [`patches/xfwm4/generate_assets.py`](./patches/xfwm4/generate_assets.py)
## CI integration
The CI system utilizes the build system, as described above, but with some unique parallelization elements to improve performance.
We have chosen to only build a limited subset of possible tweaks in CI, to constrain the time it takes to run.
Currently, we build a matrix of:
- Flavor
- Accent
The CI will run all 4 flavours in parallel (see above for precautions taken to ensure this functions correctly), and build each accent serially.
We collate the logs for these runs into files so that they can be shown neatly at the end of the run.

15
docs/CHANGELOG.md Normal file
View File

@@ -0,0 +1,15 @@
# Changelog
## [1.0.2](https://github.com/catppuccin/gtk/compare/v1.0.1...v1.0.2) (2024-05-27)
### Bug Fixes
* alt tab menu ([#209](https://github.com/catppuccin/gtk/issues/209)) ([cae57c8](https://github.com/catppuccin/gtk/commit/cae57c80f81fd1cc40fab2655109b09fa97103b9))
## [1.0.1](https://github.com/catppuccin/gtk/compare/v1.0.0...v1.0.1) (2024-05-27)
### Bug Fixes
* alt tab menu ([#209](https://github.com/catppuccin/gtk/issues/209)) ([cae57c8](https://github.com/catppuccin/gtk/commit/cae57c80f81fd1cc40fab2655109b09fa97103b9))

82
docs/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,82 @@
## Development
Information regarding the architecture of the project can be found [in the ARCHITECTURE.md](./ARCHITECTURE.md) document.
### Requirements
- All the [requirements for building](#building)
- `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
> [!TIP]
> If you need to change the patches, reset the submodule and rerun the build script.
We patch upstream colloid through a series of `.patch` files, applied through `git apply` once when the build begins.
The patches are located in `./patches/colloid/`.
Once the build script patches the submodule, it will write a file into
`colloid/.patched`, to signal to future invocations that the patches have already been applied.
The palette patches are generated through `whiskers`,
so if you're changing them, they will need regenerated. Simply run `whiskers palette.tera` to rebuild them.
The process for building the theme is [documented in the README](./README.md#building).
### Upstreaming procedure
Now and again, Colloid will have bugs upstream that impacts our theme. With our patching system we can easily fix these problems,
but we still want to contribute the fixes upstream to benefit all users & forks of Colloid.
To avoid stalling unnecessarily, our procedure for the above is as follows:
1) Open a PR to fix the issue, by adding a patch file to our theme, add `upstream:intended`
to signal these changes are to be sent to Colloid eventually.
2) Merge the PR & close the issue in our theme pertaining to the issue, once reviewed and approved
3) Open a PR in Colloid with the patch
4) Open a new issue in our theme, with these details:
- The initial issue in our theme
- The PR in Colloid that fixes the issue there
- The PR that fixed the issue in our theme
Add the `upstream:open` label
5) Once the PR is merged in Colloid:
1) Test that the issue no longer persists, without our patch
2) Open a PR to remove the patch file in our theme, with these details:
- The tracking issue
- The commit that fixed the issue in Colloid
3) Close the tracking issue & merge the PR to remove the patch file
### Running test builds
We support building and publishing test builds from PRs. When you open PRs, the CI will automatically build with your changes and push an artifact
which bundles all of the produced themes.
You can then download the artifacts as a zip (result should look similar to 7bff2448a81e36bf3b0e03bfbd649bebe6973ec7-artifacts.zip) and
pass the path into `install.py` under the `--from-artifact` option:
```bash
python3 install.py mocha blue --dest ./build --from-artifact ~/downloads/7bff2448a81e36bf3b0e03bfbd649bebe6973ec7-artifacts.zip
```
This will take the target flavor / accent out of the zip, and install it using the regular install process.
It is advised to pass a `--dest` when running in this mode, because the released zips follow the exact same naming scheme as regular builds.
This wil cause conflicts when you install, if you already had that theme installed. Passing a different destination allows you to move the
extracted folders to `~/.local/share/themes` yourself, adding a suffix as appropriate to avoid conflicts.
> [!WARNING]
> If you pass `--link` to the install script when working from a PR, it will forcibly overwrite your `~/.config/gtk-4.0/` symlinks.
> You will have to reinstall / relink to revert this.
### Useful resources
- GNOME-shell sources: https://gitlab.gnome.org/GNOME/gnome-shell/-/tree/gnome-46/data/theme
- GTK inspector guide: https://developer.gnome.org/documentation/tools/inspector.html

View File

@@ -1,6 +1,12 @@
#!/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 pathlib import Path
from dataclasses import dataclass
from urllib.request import urlopen, Request
@@ -20,6 +26,17 @@ class InstallContext:
dest: Path
link: bool
def build_info(self, include_url=True) -> str:
url = build_release_url(self)
info = f"""Installation info:
flavor: {self.flavor}
accent: {self.accent}
dest: {self.dest.absolute()}
link: {self.link}"""
if include_url:
info += f"\nremote_url: {url}"
return info
def parse_args():
parser = argparse.ArgumentParser()
@@ -49,11 +66,17 @@ def parse_args():
"sapphire",
"blue",
"lavender",
"all",
],
help="Accent of the theme.",
)
parser.add_argument(
"--from-artifact",
type=Path,
dest="from_artifact",
help="Install from an artifact instead of a mainline release, pass the artifact path (e.g 7bff2448a81e36bf3b0e03bfbd649bebe6973ec7-artifacts.zip)",
)
parser.add_argument(
"--dest",
"-d",
@@ -75,27 +98,18 @@ def parse_args():
def build_release_url(ctx: InstallContext) -> str:
repo_root = "https://github.com/catppuccin/gtk/releases/download"
release = "v1.0.0-alpha"
release = "v1.0.3" # x-release-please-version
zip_name = f"catppuccin-{ctx.flavor}-{ctx.accent}-standard+default.zip"
return f"{repo_root}/{release}/{zip_name}"
def install(ctx: InstallContext):
url = build_release_url(ctx)
build_info = f"""Installation info:
flavor: {ctx.flavor}
accent: {ctx.accent}
dest: {ctx.dest.absolute()}
link: {ctx.link}
remote_url: {url}"""
logger.info(build_info)
httprequest = Request(url)
def fetch_zip(url: str) -> Optional[zipfile.ZipFile]:
req = Request(url)
zip_file = None
logger.info("Starting download...")
with urlopen(httprequest) as response:
with urlopen(req) as response:
logger.info(f"Response status: {response.status}")
zip_file = zipfile.ZipFile(io.BytesIO(response.read()))
logger.info("Download finished, zip is valid")
@@ -104,28 +118,86 @@ def install(ctx: InstallContext):
first_bad_file = zip_file.testzip()
if first_bad_file is not None:
logger.error(f'Zip appears to be corrupt, first bad file is "{first_bad_file}"')
return
return None
logger.info("Download verified")
return zip_file
def add_libadwaita_links(ctx: InstallContext, rewrite: bool = False):
suffix = "light"
if ctx.flavor != "latte":
suffix = "dark"
dir_name = (
ctx.dest / f"catppuccin-{ctx.flavor}-{ctx.accent}-standard+default-{suffix}" / "gtk-4.0"
).absolute()
gtk4_dir = (Path(os.path.expanduser("~")) / ".config" / "gtk-4.0").absolute()
os.makedirs(gtk4_dir, exist_ok=True)
logger.info("Adding symlinks for libadwaita")
logger.info(f"Root: {dir_name}")
logger.info(f"Target: {gtk4_dir}")
try:
if rewrite:
os.remove(gtk4_dir / "assets")
os.remove(gtk4_dir / "gtk.css")
os.remove(gtk4_dir / "gtk-dark.css")
except FileNotFoundError:
logger.debug("Ignoring FileNotFound in symlink rewrite")
os.symlink(dir_name / "assets", gtk4_dir / "assets")
os.symlink(dir_name / "gtk.css", gtk4_dir / "gtk.css")
os.symlink(dir_name / "gtk-dark.css", gtk4_dir / "gtk-dark.css")
def install(ctx: InstallContext):
url = build_release_url(ctx)
logger.info(ctx.build_info())
zip_file = fetch_zip(url)
if zip_file is None:
return
logger.info("Extracting...")
zip_file.extractall(ctx.dest)
logger.info("Extraction complete")
if ctx.link:
dir_name = (ctx.dest / f"catppuccin-{ctx.flavor}-{ctx.accent}-standard+default" / 'gtk-4.0').absolute()
gtk4_dir = (Path(os.path.expanduser('~')) / '.config' / 'gtk-4.0').absolute()
os.makedirs(gtk4_dir, exist_ok=True)
add_libadwaita_links(ctx)
logger.info("Adding symlinks for libadwaita")
logger.info(f'Root: {dir_name}')
logger.info(f'Target: {gtk4_dir}')
os.symlink(dir_name / 'assets', gtk4_dir / 'assets')
os.symlink(dir_name / 'gtk.css', gtk4_dir / 'gtk.css')
os.symlink(dir_name / 'gtk-dark.css', gtk4_dir / 'gtk-dark.css')
def install_from_artifact(ctx: InstallContext, artifact_path: Path):
# Working from a pull request, special case it
logger.info(f"Extracting artifact from '{artifact_path}'")
artifacts = zipfile.ZipFile(artifact_path)
logger.info("Verifying artifact...")
first_bad_file = artifacts.testzip()
if first_bad_file is not None:
logger.error(f'Zip appears to be corrupt, first bad file is "{first_bad_file}"')
return None
logger.info("Artifact verified")
logger.info(ctx.build_info(False))
# The zip, inside the artifacts, that we want to pull out
zip_name = f"catppuccin-{ctx.flavor}-{ctx.accent}-standard+default.zip"
logger.info(f"Pulling '{zip_name}' from the artifacts")
info = artifacts.getinfo(zip_name)
logger.info("Extracting the artifact...")
artifact = zipfile.ZipFile(io.BytesIO(artifacts.read(info)))
artifact.extractall(ctx.dest)
logger.info("Extraction complete")
if ctx.link:
logger.info("Adding links (with rewrite)")
add_libadwaita_links(ctx, True)
logger.info("Links added")
def main():
args = parse_args()
dest = Path(os.path.expanduser("~")) / ".local" / "share" / "themes"
os.makedirs(dest, exist_ok=True)
@@ -136,9 +208,12 @@ def main():
flavor=args.flavor, accent=args.accent, dest=dest, link=args.link
)
install(ctx)
if args.from_artifact:
install_from_artifact(ctx, args.from_artifact)
return
logger.info('Theme installation complete!')
install(ctx)
logger.info("Theme installation complete!")
try:

View File

@@ -1,87 +0,0 @@
---
whiskers:
version: 2.1.0
matrix:
- flavor
filename: 'sass-palette-{{ flavor.identifier }}.patch'
---
{%- set palette = flavor.colors -%}
diff --git a/src/sass/_color-palette-catppuccin-{{ flavor.identifier }}.scss b/src/sass/_color-palette-catppuccin-{{ flavor.identifier }}.scss
new file mode 100644
index 00000000..8a905942
--- /dev/null
+++ b/src/sass/_color-palette-catppuccin-{{ flavor.identifier }}.scss
@@ -0,0 +1,72 @@
+// Default Theme Color Palette
+
+// Red
+$red-light: #{{ palette.red.hex }};
+$red-dark: #{{ palette.red.hex }};
+
+// Pink
+$pink-light: #{{ palette.pink.hex }};
+$pink-dark: #{{ palette.pink.hex }};
+
+// Purple
+$purple-light: #{{ palette.mauve.hex }};
+$purple-dark: #{{ palette.mauve.hex }};
+
+// Blue
+$blue-light: #{{ palette.blue.hex }};
+$blue-dark: #{{ palette.blue.hex }};
+
+// Teal
+$teal-light: #{{ palette.teal.hex }};
+$teal-dark: #{{ palette.teal.hex }};
+
+// Green
+$green-light: #{{ palette.green.hex }};
+$green-dark: #{{ palette.green.hex }};
+
+// Yellow
+$yellow-light: #{{ palette.yellow.hex }};
+$yellow-dark: #{{ palette.yellow.hex }};
+
+// Orange
+$orange-light: #{{ palette.peach.hex }};
+$orange-dark: #{{ palette.peach.hex }};
+
+// Grey
+$grey-050: #{{ palette.text.hex }};
+$grey-100: #{{ palette.subtext1.hex }};
+$grey-150: #{{ palette.subtext1.hex }};
+$grey-200: #{{ palette.subtext0.hex }};
+$grey-250: #{{ palette.subtext0.hex }};
+$grey-300: #{{ palette.overlay1.hex }};
+$grey-350: #{{ palette.overlay1.hex }};
+$grey-400: #{{ palette.overlay0.hex }};
+$grey-450: #{{ palette.overlay0.hex }};
+$grey-500: #{{ palette.surface2.hex }};
+$grey-550: #{{ palette.surface2.hex }};
+$grey-600: #{{ palette.surface1.hex }};
+$grey-650: #{{ palette.surface1.hex }};
+$grey-700: #{{ palette.surface0.hex }};
+$grey-750: #{{ palette.surface0.hex }};
+$grey-800: #{{ palette.base.hex }};
+$grey-850: #{{ palette.base.hex }};
+$grey-900: #{{ palette.crust.hex }};
+$grey-950: #{{ palette.crust.hex }};
+
+// White
+$white: #FFFFFF;
+
+// Black
+$black: #000000;
+
+// Button
+$button-close: #{{ palette.red.hex }};
+$button-max: #{{ palette.green.hex }};
+$button-min: #{{ palette.yellow.hex }};
+
+// Link
+$links: #{{ palette.sky.hex }};
+
+// Theme
+$default-light: $purple-light;
+$default-dark: $purple-dark;
{{ '' }}

View File

@@ -1,47 +0,0 @@
diff --git a/src/sass/_colors.scss b/src/sass/_colors.scss
index e8366fa0..97fa670c 100644
--- a/src/sass/_colors.scss
+++ b/src/sass/_colors.scss
@@ -57,9 +57,8 @@
@else { @return rgba($white, 0.1); }
}
-@function theme($color) {
+@function theme() {
@if ($variant == 'light') {
- @if ($theme == 'default') { @return $default-dark; }
@if ($theme == 'purple') { @return $purple-dark; }
@if ($theme == 'pink') { @return $pink-dark; }
@if ($theme == 'red') { @return $red-dark; }
@@ -68,8 +67,9 @@
@if ($theme == 'green') { @return $green-dark; }
@if ($theme == 'teal') { @return $teal-dark; }
@if ($theme == 'grey') { @return $grey-600; }
+
+ @return $default-dark;
} @else {
- @if ($theme == 'default') { @return $default-light; }
@if ($theme == 'purple') { @return $purple-light; }
@if ($theme == 'pink') { @return $pink-light; }
@if ($theme == 'red') { @return $red-light; }
@@ -78,6 +78,8 @@
@if ($theme == 'green') { @return $green-light; }
@if ($theme == 'teal') { @return $teal-light; }
@if ($theme == 'grey') { @return $grey-200; }
+
+ @return $default-light;
}
}
@@ -113,9 +115,9 @@
// Basic colors
//
-$primary: theme(color);
+$primary: theme();
$drop_target_color: $orange-dark;
-$indicator: theme(color);
+$indicator: theme();
$titlebar-indicator: if($variant == 'dark', currentColor, $primary);
$inverse-indicator: if($theme == 'grey', $white, $primary);
$applet-primary: if($theme == 'grey' and variant == 'light' and $topbar == 'dark', $grey-200, $primary);

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 49 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 49 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 49 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 49 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 49 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -0,0 +1,15 @@
{
"last-release-sha": "8b54c05ff5de6c7fabc68fab410cfd093a91193c",
"draft": true,
"packages": {
".": {
"package-name": "",
"release-type": "simple",
"extra-files": [
{ "type": "generic", "path": "README.md" },
{ "type": "generic", "path": "install.py" }
]
}
},
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
}

9
renovate.json Normal file
View File

@@ -0,0 +1,9 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"git-submodules": {
"enabled": true
}
}

View File

@@ -7,5 +7,8 @@ pkgs.mkShell {
python311
python311Packages.catppuccin
sassc
inkscape
optipng
ruff
];
}

74
sources/build/__init__.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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")

View File

@@ -0,0 +1,13 @@
diff --git a/src/sass/gnome-shell/common/_switcher-popup.scss b/src/sass/gnome-shell/common/_switcher-popup.scss
index 90b51ab4..408662fb 100644
--- a/src/sass/gnome-shell/common/_switcher-popup.scss
+++ b/src/sass/gnome-shell/common/_switcher-popup.scss
@@ -14,6 +14,8 @@
border-radius: $base_radius;
border: 1px solid transparent;
+ background-color: $osd;
+
&:outlined {
background-color: $divider;
color: $text; // for Ubuntu session

View File

@@ -0,0 +1,123 @@
---
whiskers:
version: 2.1.0
matrix:
- flavor
filename: 'sass-palette-{{ flavor.identifier }}.patch'
---
{%- set palette = flavor.colors -%}
diff --git a/src/sass/_color-palette-catppuccin-{{ flavor.identifier }}.scss b/src/sass/_color-palette-catppuccin-{{ flavor.identifier }}.scss
new file mode 100644
index 00000000..4ff0da0d
--- /dev/null
+++ b/src/sass/_color-palette-catppuccin-{{ flavor.identifier }}.scss
@@ -0,0 +1,87 @@
+ // Our accents
+ $rosewater: #{{ palette.rosewater.hex }};
+ $flamingo: #{{ palette.flamingo.hex }};
+ $pink: #{{ palette.pink.hex }};
+ $mauve: #{{ palette.mauve.hex }};
+ $red: #{{ palette.red.hex }};
+ $maroon: #{{ palette.maroon.hex }};
+ $peach: #{{ palette.peach.hex }};
+ $yellow: #{{ palette.yellow.hex }};
+ $green: #{{ palette.green.hex }};
+ $teal: #{{ palette.teal.hex }};
+ $sky: #{{ palette.sky.hex }};
+ $sapphire: #{{ palette.sapphire.hex }};
+ $blue: #{{ palette.blue.hex }};
+ $lavender: #{{ palette.lavender.hex }};
+// Default Theme Color Palette
+
+// Red
+$red-light: #{{ palette.red.hex }};
+$red-dark: #{{ palette.red.hex }};
+
+// Pink
+$pink-light: #{{ palette.pink.hex }};
+$pink-dark: #{{ palette.pink.hex }};
+
+// Purple
+$purple-light: #{{ palette.mauve.hex }};
+$purple-dark: #{{ palette.mauve.hex }};
+
+// Blue
+$blue-light: #{{ palette.blue.hex }};
+$blue-dark: #{{ palette.blue.hex }};
+
+// Teal
+$teal-light: #{{ palette.teal.hex }};
+$teal-dark: #{{ palette.teal.hex }};
+
+// Green
+$green-light: #{{ palette.green.hex }};
+$green-dark: #{{ palette.green.hex }};
+
+// Yellow
+$yellow-light: #{{ palette.yellow.hex }};
+$yellow-dark: #{{ palette.yellow.hex }};
+
+// Orange
+$orange-light: #{{ palette.peach.hex }};
+$orange-dark: #{{ palette.peach.hex }};
+
+// Grey
{% if flavor.dark -%} {#- https://github.com/catppuccin/gtk/blob/0c3e8817da94769887c690b2211e006b287b27b1/scripts/recolor.py#L153-L176 -#}
+$grey-050: #{{ palette.overlay2.hex }};
+$grey-100: #{{ palette.overlay1.hex }};
+$grey-150: #{{ palette.overlay0.hex }};
+$grey-200: #{{ palette.surface2.hex }};
+$grey-250: #{{ palette.surface1.hex }};
+$grey-300: red;
+$grey-350: red;
+$grey-400: red;
+$grey-450: red;
+$grey-500: red;
+$grey-550: red;
+$grey-600: red;
+$grey-650: #{{ palette.surface0.hex }};
+$grey-700: #{{ palette.base.hex }};
+$grey-750: #{{ palette.mantle.hex }};
+$grey-800: #{{ palette.crust.hex }};
+$grey-850: #020202;
+$grey-900: #010101;
+$grey-950: #000000;
{%- else -%} {#- Light mode https://github.com/catppuccin/gtk/blob/0c3e8817da94769887c690b2211e006b287b27b1/scripts/recolor.py#L128-L151 -#}
+$grey-050: #{{ palette.crust.hex }};
+$grey-100: #{{ palette.mantle.hex }};
+$grey-150: #{{ palette.base.hex }};
+$grey-200: #{{ palette.surface0.hex }};
+$grey-250: #{{ palette.surface1.hex }};
+$grey-300: red;
+$grey-350: red;
+$grey-400: red;
+$grey-450: red;
+$grey-500: red;
+$grey-550: red;
+$grey-600: red;
+$grey-650: #{{ palette.base.hex }};
+$grey-700: #{{ palette.base.hex }};
+$grey-750: #{{ palette.mantle.hex }};
+$grey-800: #{{ palette.text.hex }};
+$grey-850: #020202;
+$grey-900: #010101;
+$grey-950: #000000;
{%- endif %}
+
+// White
+$white: #eff1f5;
+
+// Black
+$black: #11111b;
+
+// Button
+$button-close: #{{ palette.red.hex }};
+$button-max: #{{ palette.green.hex }};
+$button-min: #{{ palette.yellow.hex }};
+
+// Link
+$links: #{{ palette.sky.hex }};
+
+// Theme
+$default-light: $purple-light;
+$default-dark: $purple-dark;

View File

@@ -1,9 +1,24 @@
diff --git a/src/sass/_color-palette-catppuccin-frappe.scss b/src/sass/_color-palette-catppuccin-frappe.scss
new file mode 100644
index 00000000..8a905942
index 00000000..4ff0da0d
--- /dev/null
+++ b/src/sass/_color-palette-catppuccin-frappe.scss
@@ -0,0 +1,72 @@
@@ -0,0 +1,87 @@
+ // Our accents
+ $rosewater: #f2d5cf;
+ $flamingo: #eebebe;
+ $pink: #f4b8e4;
+ $mauve: #ca9ee6;
+ $red: #e78284;
+ $maroon: #ea999c;
+ $peach: #ef9f76;
+ $yellow: #e5c890;
+ $green: #a6d189;
+ $teal: #81c8be;
+ $sky: #99d1db;
+ $sapphire: #85c1dc;
+ $blue: #8caaee;
+ $lavender: #babbf1;
+// Default Theme Color Palette
+
+// Red
@@ -39,31 +54,31 @@ index 00000000..8a905942
+$orange-dark: #ef9f76;
+
+// Grey
+$grey-050: #c6d0f5;
+$grey-100: #b5bfe2;
+$grey-150: #b5bfe2;
+$grey-200: #a5adce;
+$grey-250: #a5adce;
+$grey-300: #838ba7;
+$grey-350: #838ba7;
+$grey-400: #737994;
+$grey-450: #737994;
+$grey-500: #626880;
+$grey-550: #626880;
+$grey-600: #51576d;
+$grey-650: #51576d;
+$grey-700: #414559;
+$grey-750: #414559;
+$grey-800: #303446;
+$grey-850: #303446;
+$grey-900: #232634;
+$grey-950: #232634;
+$grey-050: #949cbb;
+$grey-100: #838ba7;
+$grey-150: #737994;
+$grey-200: #626880;
+$grey-250: #51576d;
+$grey-300: red;
+$grey-350: red;
+$grey-400: red;
+$grey-450: red;
+$grey-500: red;
+$grey-550: red;
+$grey-600: red;
+$grey-650: #414559;
+$grey-700: #303446;
+$grey-750: #292c3c;
+$grey-800: #232634;
+$grey-850: #020202;
+$grey-900: #010101;
+$grey-950: #000000;
+
+// White
+$white: #FFFFFF;
+$white: #eff1f5;
+
+// Black
+$black: #000000;
+$black: #11111b;
+
+// Button
+$button-close: #e78284;

View File

@@ -1,9 +1,24 @@
diff --git a/src/sass/_color-palette-catppuccin-latte.scss b/src/sass/_color-palette-catppuccin-latte.scss
new file mode 100644
index 00000000..8a905942
index 00000000..4ff0da0d
--- /dev/null
+++ b/src/sass/_color-palette-catppuccin-latte.scss
@@ -0,0 +1,72 @@
@@ -0,0 +1,87 @@
+ // Our accents
+ $rosewater: #dc8a78;
+ $flamingo: #dd7878;
+ $pink: #ea76cb;
+ $mauve: #8839ef;
+ $red: #d20f39;
+ $maroon: #e64553;
+ $peach: #fe640b;
+ $yellow: #df8e1d;
+ $green: #40a02b;
+ $teal: #179299;
+ $sky: #04a5e5;
+ $sapphire: #209fb5;
+ $blue: #1e66f5;
+ $lavender: #7287fd;
+// Default Theme Color Palette
+
+// Red
@@ -39,31 +54,31 @@ index 00000000..8a905942
+$orange-dark: #fe640b;
+
+// Grey
+$grey-050: #4c4f69;
+$grey-100: #5c5f77;
+$grey-150: #5c5f77;
+$grey-200: #6c6f85;
+$grey-250: #6c6f85;
+$grey-300: #8c8fa1;
+$grey-350: #8c8fa1;
+$grey-400: #9ca0b0;
+$grey-450: #9ca0b0;
+$grey-500: #acb0be;
+$grey-550: #acb0be;
+$grey-600: #bcc0cc;
+$grey-650: #bcc0cc;
+$grey-700: #ccd0da;
+$grey-750: #ccd0da;
+$grey-800: #eff1f5;
+$grey-850: #eff1f5;
+$grey-900: #dce0e8;
+$grey-950: #dce0e8;
+$grey-050: #dce0e8;
+$grey-100: #e6e9ef;
+$grey-150: #eff1f5;
+$grey-200: #ccd0da;
+$grey-250: #bcc0cc;
+$grey-300: red;
+$grey-350: red;
+$grey-400: red;
+$grey-450: red;
+$grey-500: red;
+$grey-550: red;
+$grey-600: red;
+$grey-650: #eff1f5;
+$grey-700: #eff1f5;
+$grey-750: #e6e9ef;
+$grey-800: #4c4f69;
+$grey-850: #020202;
+$grey-900: #010101;
+$grey-950: #000000;
+
+// White
+$white: #FFFFFF;
+$white: #eff1f5;
+
+// Black
+$black: #000000;
+$black: #11111b;
+
+// Button
+$button-close: #d20f39;

View File

@@ -1,9 +1,24 @@
diff --git a/src/sass/_color-palette-catppuccin-macchiato.scss b/src/sass/_color-palette-catppuccin-macchiato.scss
new file mode 100644
index 00000000..8a905942
index 00000000..4ff0da0d
--- /dev/null
+++ b/src/sass/_color-palette-catppuccin-macchiato.scss
@@ -0,0 +1,72 @@
@@ -0,0 +1,87 @@
+ // Our accents
+ $rosewater: #f4dbd6;
+ $flamingo: #f0c6c6;
+ $pink: #f5bde6;
+ $mauve: #c6a0f6;
+ $red: #ed8796;
+ $maroon: #ee99a0;
+ $peach: #f5a97f;
+ $yellow: #eed49f;
+ $green: #a6da95;
+ $teal: #8bd5ca;
+ $sky: #91d7e3;
+ $sapphire: #7dc4e4;
+ $blue: #8aadf4;
+ $lavender: #b7bdf8;
+// Default Theme Color Palette
+
+// Red
@@ -39,31 +54,31 @@ index 00000000..8a905942
+$orange-dark: #f5a97f;
+
+// Grey
+$grey-050: #cad3f5;
+$grey-100: #b8c0e0;
+$grey-150: #b8c0e0;
+$grey-200: #a5adcb;
+$grey-250: #a5adcb;
+$grey-300: #8087a2;
+$grey-350: #8087a2;
+$grey-400: #6e738d;
+$grey-450: #6e738d;
+$grey-500: #5b6078;
+$grey-550: #5b6078;
+$grey-600: #494d64;
+$grey-650: #494d64;
+$grey-700: #363a4f;
+$grey-750: #363a4f;
+$grey-800: #24273a;
+$grey-850: #24273a;
+$grey-900: #181926;
+$grey-950: #181926;
+$grey-050: #939ab7;
+$grey-100: #8087a2;
+$grey-150: #6e738d;
+$grey-200: #5b6078;
+$grey-250: #494d64;
+$grey-300: red;
+$grey-350: red;
+$grey-400: red;
+$grey-450: red;
+$grey-500: red;
+$grey-550: red;
+$grey-600: red;
+$grey-650: #363a4f;
+$grey-700: #24273a;
+$grey-750: #1e2030;
+$grey-800: #181926;
+$grey-850: #020202;
+$grey-900: #010101;
+$grey-950: #000000;
+
+// White
+$white: #FFFFFF;
+$white: #eff1f5;
+
+// Black
+$black: #000000;
+$black: #11111b;
+
+// Button
+$button-close: #ed8796;

View File

@@ -1,9 +1,24 @@
diff --git a/src/sass/_color-palette-catppuccin-mocha.scss b/src/sass/_color-palette-catppuccin-mocha.scss
new file mode 100644
index 00000000..8a905942
index 00000000..4ff0da0d
--- /dev/null
+++ b/src/sass/_color-palette-catppuccin-mocha.scss
@@ -0,0 +1,72 @@
@@ -0,0 +1,87 @@
+ // Our accents
+ $rosewater: #f5e0dc;
+ $flamingo: #f2cdcd;
+ $pink: #f5c2e7;
+ $mauve: #cba6f7;
+ $red: #f38ba8;
+ $maroon: #eba0ac;
+ $peach: #fab387;
+ $yellow: #f9e2af;
+ $green: #a6e3a1;
+ $teal: #94e2d5;
+ $sky: #89dceb;
+ $sapphire: #74c7ec;
+ $blue: #89b4fa;
+ $lavender: #b4befe;
+// Default Theme Color Palette
+
+// Red
@@ -39,31 +54,31 @@ index 00000000..8a905942
+$orange-dark: #fab387;
+
+// Grey
+$grey-050: #cdd6f4;
+$grey-100: #bac2de;
+$grey-150: #bac2de;
+$grey-200: #a6adc8;
+$grey-250: #a6adc8;
+$grey-300: #7f849c;
+$grey-350: #7f849c;
+$grey-400: #6c7086;
+$grey-450: #6c7086;
+$grey-500: #585b70;
+$grey-550: #585b70;
+$grey-600: #45475a;
+$grey-650: #45475a;
+$grey-700: #313244;
+$grey-750: #313244;
+$grey-800: #1e1e2e;
+$grey-850: #1e1e2e;
+$grey-900: #11111b;
+$grey-950: #11111b;
+$grey-050: #9399b2;
+$grey-100: #7f849c;
+$grey-150: #6c7086;
+$grey-200: #585b70;
+$grey-250: #45475a;
+$grey-300: red;
+$grey-350: red;
+$grey-400: red;
+$grey-450: red;
+$grey-500: red;
+$grey-550: red;
+$grey-600: red;
+$grey-650: #313244;
+$grey-700: #1e1e2e;
+$grey-750: #181825;
+$grey-800: #11111b;
+$grey-850: #020202;
+$grey-900: #010101;
+$grey-950: #000000;
+
+// White
+$white: #FFFFFF;
+$white: #eff1f5;
+
+// Black
+$black: #000000;
+$black: #11111b;
+
+// Button
+$button-close: #f38ba8;

View File

@@ -0,0 +1,46 @@
diff --git a/src/sass/_colors.scss b/src/sass/_colors.scss
index 8738bf37..71797c6d 100644
--- a/src/sass/_colors.scss
+++ b/src/sass/_colors.scss
@@ -58,27 +58,20 @@
}
@function theme($color) {
- @if ($variant == 'light') {
- @if ($theme == 'default') { @return $default-dark; }
- @if ($theme == 'purple') { @return $purple-dark; }
- @if ($theme == 'pink') { @return $pink-dark; }
- @if ($theme == 'red') { @return $red-dark; }
- @if ($theme == 'orange') { @return $orange-dark; }
- @if ($theme == 'yellow') { @return $yellow-dark; }
- @if ($theme == 'green') { @return $green-dark; }
- @if ($theme == 'teal') { @return $teal-dark; }
- @if ($theme == 'grey') { @return $grey-600; }
- } @else {
- @if ($theme == 'default') { @return $default-light; }
- @if ($theme == 'purple') { @return $purple-light; }
- @if ($theme == 'pink') { @return $pink-light; }
- @if ($theme == 'red') { @return $red-light; }
- @if ($theme == 'orange') { @return $orange-light; }
- @if ($theme == 'yellow') { @return $yellow-light; }
- @if ($theme == 'green') { @return $green-light; }
- @if ($theme == 'teal') { @return $teal-light; }
- @if ($theme == 'grey') { @return $grey-200; }
- }
+ @if ($theme == 'rosewater') { @return $rosewater; }
+ @if ($theme == 'flamingo') { @return $flamingo; }
+ @if ($theme == 'pink') { @return $pink; }
+ @if ($theme == 'mauve') { @return $mauve; }
+ @if ($theme == 'red') { @return $red; }
+ @if ($theme == 'maroon') { @return $maroon; }
+ @if ($theme == 'peach') { @return $peach; }
+ @if ($theme == 'yellow') { @return $yellow; }
+ @if ($theme == 'green') { @return $green; }
+ @if ($theme == 'teal') { @return $teal; }
+ @if ($theme == 'sky') { @return $sky; }
+ @if ($theme == 'sapphire') { @return $sapphire; }
+ @if ($theme == 'blue') { @return $blue; }
+ @if ($theme == 'lavender') { @return $lavender; }
}
@function background($type) {

2
sources/patches/xfwm4/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
generated/
patched/

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 57 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 47 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 57 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -0,0 +1,250 @@
from typing import List
from catppuccin import PALETTE
from catppuccin.models import Flavor
import re
import os
import shutil
import subprocess
import time
from dataclasses import dataclass
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
INDEX = [
"close-active",
"close-inactive",
"close-prelight",
"close-pressed",
"hide-active",
"hide-inactive",
"hide-prelight",
"hide-pressed",
"maximize-active",
"maximize-inactive",
"maximize-prelight",
"maximize-pressed",
"maximize-toggled-active",
"maximize-toggled-inactive",
"maximize-toggled-prelight",
"maximize-toggled-pressed",
"menu-active",
"menu-inactive",
"menu-prelight",
"menu-pressed",
"shade-active",
"shade-inactive",
"shade-prelight",
"shade-pressed",
"shade-toggled-active",
"shade-toggled-inactive",
"shade-toggled-prelight",
"shade-toggled-pressed",
"stick-active",
"stick-inactive",
"stick-prelight",
"stick-pressed",
"stick-toggled-active",
"stick-toggled-inactive",
"stick-toggled-prelight",
"stick-toggled-pressed",
"title-1-active",
"title-1-inactive",
"title-2-active",
"title-2-inactive",
"title-3-active",
"title-3-inactive",
"title-4-active",
"title-4-inactive",
"title-5-active",
"title-5-inactive",
"top-left-active",
"top-left-inactive",
"top-right-active",
"top-right-inactive",
"left-active",
"left-inactive",
"right-active",
"right-inactive",
"bottom-active",
"bottom-inactive",
"bottom-left-active",
"bottom-left-inactive",
"bottom-right-active",
"bottom-right-inactive",
]
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))
def generate_for_flavor(flavor: Flavor):
# Setup the palette
palette = flavor.colors
close_color = f"#{palette.red.hex}"
max_color = f"#{palette.green.hex}"
min_color = f"#{palette.yellow.hex}"
# 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
# and the rest get the -dark assets to work from
color = "-dark"
if not flavor.dark:
color = "-light"
# Make a directory for our patched SVGs to live in before compilation
odir = f"{THIS_DIR}/patched"
os.makedirs(odir, exist_ok=True)
# Copy the base assets into the output
shutil.copy(
f"{THIS_DIR}/assets{color}.svg",
f"{odir}/assets-catppuccin-{flavor.identifier}.svg",
)
shutil.copy(
f"{THIS_DIR}/assets{color}-normal.svg",
f"{odir}/assets-catppuccin-{flavor.identifier}-normal.svg",
)
# Patch all the SVGs
path = f"{odir}/assets-catppuccin-{flavor.identifier}-normal.svg"
subst_text(path, "#fd5f51", close_color)
subst_text(path, "#38c76a", max_color)
subst_text(path, "#fdbe04", min_color)
headerbar = palette.base.hex
headerbar_backdrop = palette.mantle.hex
if flavor.dark:
path = f"{odir}/assets-catppuccin-{flavor.identifier}.svg"
subst_text(path, "#242424", headerbar)
subst_text(path, "#2c2c2c", headerbar_backdrop)
path = f"{odir}/assets-catppuccin-{flavor.identifier}-normal.svg"
subst_text(path, "#242424", headerbar)
subst_text(path, "#2c2c2c", headerbar_backdrop)
else:
path = f"{odir}/assets-catppuccin-{flavor.identifier}.svg"
subst_text(path, "#f2f2f2", headerbar)
subst_text(path, "#fafafa", headerbar_backdrop)
path = f"{odir}/assets-catppuccin-{flavor.identifier}-normal.svg"
subst_text(path, "#f2f2f2", headerbar)
subst_text(path, "#fafafa", headerbar_backdrop)
@dataclass
class WorkerInput:
output_path: str
output_dir: str
input_path: str
dpi: str
ident: str
def call_subprocesses(inp: WorkerInput):
inkscape = "inkscape"
optipng = "optipng"
return [
subprocess.Popen(
[
# FIXME: https://gitlab.com/inkscape/inkscape/-/issues/4716#note_1882967469Z
"unshare",
"--user",
inkscape,
"--export-id",
inp.ident,
"--export-id-only",
"--export-dpi",
inp.dpi,
"--export-filename",
inp.output_path,
inp.input_path,
],
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT,
),
subprocess.Popen(
[optipng, "-o7", "--quiet", inp.output_path],
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT,
),
]
@dataclass
class RenderState:
tasks: List[subprocess.Popen]
input_root: str
output_root: str
screen_to_dpi = {
"-hdpi": "144",
"-xhdpi": "192",
}
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
# a stupid amount of compute and time for little benefit
src_file = f"{state.input_root}/assets-catppuccin-{flavor.identifier}.svg"
output_dir = f"{state.output_root}/assets-catppuccin-{flavor.identifier}{screen}"
output_path = f"{output_dir}/{ident}.png"
dpi = screen_to_dpi.get(screen, "96")
os.makedirs(output_dir, exist_ok=True)
if os.path.exists(output_path):
print(f"Skipping '{output_path}', already generated")
else:
new_tasks = call_subprocesses(
WorkerInput(
output_path=output_path,
output_dir=output_dir,
input_path=src_file,
dpi=dpi,
ident=ident,
)
)
state.tasks.extend(new_tasks)
def render_for_flavor(flavor: Flavor, state: RenderState):
print(f"Starting render tasks for {flavor.identifier}")
for ident in INDEX:
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="-xhdpi", ident=ident)
generate_for_flavor(PALETTE.mocha)
generate_for_flavor(PALETTE.latte)
generate_for_flavor(PALETTE.macchiato)
generate_for_flavor(PALETTE.frappe)
state = RenderState(
tasks=[], input_root=f"{THIS_DIR}/patched", output_root=f"{THIS_DIR}/generated"
)
start_time = time.time()
render_for_flavor(PALETTE.mocha, state)
render_for_flavor(PALETTE.latte, state)
render_for_flavor(PALETTE.macchiato, state)
render_for_flavor(PALETTE.frappe, state)
for task in state.tasks:
task.wait()
end_time = time.time() - start_time
print(f"Generation complete in {end_time} seconds")
shutil.rmtree(state.input_root)