mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
Compare commits
129 Commits
displaycon
...
bbd57e0673
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbd57e0673 | ||
|
|
b1632a0a03 | ||
|
|
7aff1182c8 | ||
|
|
fbe362cd20 | ||
|
|
e7f94c94cc | ||
|
|
7523190b16 | ||
|
|
da37e16b6e | ||
|
|
5c420ab50b | ||
|
|
4493b7c231 | ||
|
|
40a96c6eaf | ||
|
|
23a93082c6 | ||
|
|
f7650b5e1f | ||
|
|
3ebdd5631c | ||
|
|
6c4caf121a | ||
|
|
89788e9ca7 | ||
|
|
0787c63fed | ||
|
|
9fc0d5efff | ||
|
|
6611dfbe05 | ||
|
|
8a71ead51d | ||
|
|
d9d6ab5776 | ||
|
|
d6fe7bea27 | ||
|
|
1194f3ffb8 | ||
|
|
5ac81e6dd6 | ||
|
|
987856a1de | ||
|
|
ef52ce0990 | ||
|
|
06b14a5869 | ||
|
|
fd839059c0 | ||
|
|
ec6db7962a | ||
|
|
adf92cbc46 | ||
|
|
6b6f51cd1f | ||
|
|
df6c60213f | ||
|
|
6303304a10 | ||
|
|
8e76789119 | ||
|
|
10e81cfdd3 | ||
|
|
03fd3a4f16 | ||
|
|
8fdc748ed2 | ||
|
|
6c56d23b93 | ||
|
|
45d34dcb5b | ||
|
|
d7ac0d50fa | ||
|
|
1d4d145187 | ||
|
|
a5b9ff98c0 | ||
|
|
6feaecd92e | ||
|
|
b066a25308 | ||
|
|
777a552b57 | ||
|
|
7dbe608c28 | ||
|
|
61630e447b | ||
|
|
91385e7c83 | ||
|
|
04648fcca7 | ||
|
|
080fc7e44e | ||
|
|
0b60da3d6d | ||
|
|
a4492b90e7 | ||
|
|
c9331b7338 | ||
|
|
4982ea53dd | ||
|
|
c703cb6504 | ||
|
|
a7494971fd | ||
|
|
c548255bfc | ||
|
|
9656c7afd7 | ||
|
|
414b8c8272 | ||
|
|
b4f83d09d4 | ||
|
|
67ee74ac20 | ||
|
|
93539d2b6b | ||
|
|
524d967745 | ||
|
|
0effbebbb6 | ||
|
|
dca07a70f8 | ||
|
|
02936c97fd | ||
|
|
8f7e732827 | ||
|
|
5ffe563b7d | ||
|
|
6ef08c3d54 | ||
|
|
908b4b58cd | ||
|
|
f2611e0de0 | ||
|
|
ea75a9d351 | ||
|
|
3a744d7d68 | ||
|
|
195d312ae2 | ||
|
|
76006a7377 | ||
|
|
11536da512 | ||
|
|
2a91bc41f7 | ||
|
|
baf23157fc | ||
|
|
83b81be825 | ||
|
|
4aefa0f1f7 | ||
|
|
e53a7cee97 | ||
|
|
8437e1aa7b | ||
|
|
632f40cc0a | ||
|
|
7d81445341 | ||
|
|
78a5f401d7 | ||
|
|
8745f98c95 | ||
|
|
f0f5bcc630 | ||
|
|
8a3c513605 | ||
|
|
145d2636dd | ||
|
|
f2b9dc8988 | ||
|
|
2e4d56728b | ||
|
|
18231ed324 | ||
|
|
d0b61d8ed1 | ||
|
|
d385a44949 | ||
|
|
d97392d46e | ||
|
|
6abb2c73fd | ||
|
|
7e141c6b36 | ||
|
|
53553c1f62 | ||
|
|
523ccc6bf8 | ||
|
|
811e89fcfa | ||
|
|
5d5be4d9d7 | ||
|
|
88457ab139 | ||
|
|
0034926df7 | ||
|
|
d082d41ab9 | ||
|
|
b7911475b6 | ||
|
|
672754b0b5 | ||
|
|
0d1553123b | ||
|
|
ba6c51c102 | ||
|
|
d64206a9ff | ||
|
|
d9a1089039 | ||
|
|
55fe463405 | ||
|
|
e84210e962 | ||
|
|
ff506548d3 | ||
|
|
f6b09751e9 | ||
|
|
3d863979c4 | ||
|
|
2947ff4131 | ||
|
|
b8fca10896 | ||
|
|
33e45794d2 | ||
|
|
42cc88ca65 | ||
|
|
0b7f2416ca | ||
|
|
5d5c745ee5 | ||
|
|
e0429e4c60 | ||
|
|
0bece5287e | ||
|
|
60b5e47836 | ||
|
|
aa75b44790 | ||
|
|
769f58caa9 | ||
|
|
e7facf740d | ||
|
|
04921eef62 | ||
|
|
8863c42879 | ||
|
|
2745116ac5 |
294
.github/workflows/release.yml
vendored
294
.github/workflows/release.yml
vendored
@@ -398,297 +398,3 @@ jobs:
|
||||
prerelease: ${{ contains(env.TAG, '-') }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# trigger-obs-update:
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: release
|
||||
# env:
|
||||
# TAG: ${{ inputs.tag }}
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
# with:
|
||||
# ref: ${{ inputs.tag }}
|
||||
|
||||
# - name: Install OSC
|
||||
# run: |
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y osc
|
||||
|
||||
# mkdir -p ~/.config/osc
|
||||
# cat > ~/.config/osc/oscrc << EOF
|
||||
# [general]
|
||||
# apiurl = https://api.opensuse.org
|
||||
|
||||
# [https://api.opensuse.org]
|
||||
# user = ${{ secrets.OBS_USERNAME }}
|
||||
# pass = ${{ secrets.OBS_PASSWORD }}
|
||||
# EOF
|
||||
# chmod 600 ~/.config/osc/oscrc
|
||||
|
||||
# - name: Update OBS packages
|
||||
# run: |
|
||||
# cd distro
|
||||
# bash scripts/obs-upload.sh dms "Update to ${TAG}"
|
||||
|
||||
# trigger-ppa-update:
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: release
|
||||
# env:
|
||||
# TAG: ${{ inputs.tag }}
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
# with:
|
||||
# ref: ${{ inputs.tag }}
|
||||
|
||||
# - name: Install build dependencies
|
||||
# run: |
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y \
|
||||
# debhelper \
|
||||
# devscripts \
|
||||
# dput \
|
||||
# lftp \
|
||||
# build-essential \
|
||||
# fakeroot \
|
||||
# dpkg-dev
|
||||
|
||||
# - name: Configure GPG
|
||||
# env:
|
||||
# GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
# run: |
|
||||
# echo "$GPG_KEY" | gpg --import
|
||||
# GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | awk '{print $2}' | cut -d'/' -f2)
|
||||
# echo "DEBSIGN_KEYID=$GPG_KEY_ID" >> $GITHUB_ENV
|
||||
|
||||
# - name: Upload to PPA
|
||||
# run: |
|
||||
# cd distro/ubuntu/ppa
|
||||
# bash create-and-upload.sh ../dms dms questing
|
||||
|
||||
# copr-build:
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: release
|
||||
# env:
|
||||
# TAG: ${{ inputs.tag }}
|
||||
|
||||
# steps:
|
||||
# - name: Checkout repository
|
||||
# uses: actions/checkout@v4
|
||||
# with:
|
||||
# ref: ${{ inputs.tag }}
|
||||
|
||||
# - name: Determine version
|
||||
# id: version
|
||||
# run: |
|
||||
# VERSION="${TAG#v}"
|
||||
# echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
# echo "Building DMS stable version: $VERSION"
|
||||
|
||||
# - name: Setup build environment
|
||||
# run: |
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y rpm wget curl jq gzip
|
||||
# mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||
|
||||
# - name: Download release assets
|
||||
# run: |
|
||||
# VERSION="${{ steps.version.outputs.version }}"
|
||||
# cd ~/rpmbuild/SOURCES
|
||||
|
||||
# wget "https://github.com/AvengeMedia/DankMaterialShell/releases/download/v${VERSION}/dms-qml.tar.gz" || {
|
||||
# echo "Failed to download dms-qml.tar.gz for v${VERSION}"
|
||||
# exit 1
|
||||
# }
|
||||
|
||||
# - name: Generate stable spec file
|
||||
# run: |
|
||||
# VERSION="${{ steps.version.outputs.version }}"
|
||||
# CHANGELOG_DATE="$(date '+%a %b %d %Y')"
|
||||
|
||||
# cat > ~/rpmbuild/SPECS/dms.spec <<'SPECEOF'
|
||||
# # Spec for DMS stable releases - Generated by GitHub Actions
|
||||
|
||||
# %global debug_package %{nil}
|
||||
# %global version VERSION_PLACEHOLDER
|
||||
# %global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||
|
||||
# Name: dms
|
||||
# Version: %{version}
|
||||
# Release: 1%{?dist}
|
||||
# Summary: %{pkg_summary}
|
||||
|
||||
# License: MIT
|
||||
# URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
|
||||
# Source0: dms-qml.tar.gz
|
||||
|
||||
# BuildRequires: gzip
|
||||
# BuildRequires: wget
|
||||
# BuildRequires: systemd-rpm-macros
|
||||
|
||||
# Requires: (quickshell or quickshell-git)
|
||||
# Requires: accountsservice
|
||||
# Requires: dms-cli = %{version}-%{release}
|
||||
# Requires: dgop
|
||||
|
||||
# Recommends: cava
|
||||
# Recommends: cliphist
|
||||
# Recommends: danksearch
|
||||
# Recommends: matugen
|
||||
# Recommends: wl-clipboard
|
||||
# Recommends: NetworkManager
|
||||
# Recommends: qt6-qtmultimedia
|
||||
# Suggests: qt6ct
|
||||
|
||||
# %description
|
||||
# DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
||||
# and optimized for the niri and hyprland compositors. Features notifications,
|
||||
# app launcher, wallpaper customization, and fully customizable with plugins.
|
||||
|
||||
# Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
|
||||
# process monitoring, notification center, clipboard history, dock, control center,
|
||||
# lock screen, and comprehensive plugin system.
|
||||
|
||||
# %package -n dms-cli
|
||||
# Summary: DankMaterialShell CLI tool
|
||||
# License: MIT
|
||||
# URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
|
||||
# %description -n dms-cli
|
||||
# Command-line interface for DankMaterialShell configuration and management.
|
||||
# Provides native DBus bindings, NetworkManager integration, and system utilities.
|
||||
|
||||
# %prep
|
||||
# %setup -q -c -n dms-qml
|
||||
|
||||
# # Download architecture-specific binaries during build
|
||||
# case "%{_arch}" in
|
||||
# x86_64)
|
||||
# ARCH_SUFFIX="amd64"
|
||||
# ;;
|
||||
# aarch64)
|
||||
# ARCH_SUFFIX="arm64"
|
||||
# ;;
|
||||
# *)
|
||||
# echo "Unsupported architecture: %{_arch}"
|
||||
# exit 1
|
||||
# ;;
|
||||
# esac
|
||||
|
||||
# wget -O %{_builddir}/dms-cli.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-${ARCH_SUFFIX}.gz" || {
|
||||
# echo "Failed to download dms-cli for architecture %{_arch}"
|
||||
# exit 1
|
||||
# }
|
||||
# gunzip -c %{_builddir}/dms-cli.gz > %{_builddir}/dms-cli
|
||||
# chmod +x %{_builddir}/dms-cli
|
||||
|
||||
# %build
|
||||
|
||||
# %install
|
||||
# install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms
|
||||
|
||||
# install -d %{buildroot}%{_datadir}/bash-completion/completions
|
||||
# install -d %{buildroot}%{_datadir}/zsh/site-functions
|
||||
# install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
||||
# %{_builddir}/dms-cli completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||
# %{_builddir}/dms-cli completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||
# %{_builddir}/dms-cli completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||
|
||||
# install -Dm644 assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||
|
||||
# install -Dm644 assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop
|
||||
# install -Dm644 assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||
|
||||
# install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||
# cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||
|
||||
# rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||
# rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
# rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
# rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
|
||||
|
||||
# echo "%{version}" > %{buildroot}%{_datadir}/quickshell/dms/VERSION
|
||||
|
||||
# %posttrans
|
||||
# if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||
# rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||
# rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
# rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
# fi
|
||||
# # Signal running DMS instances to reload
|
||||
# pkill -USR1 -x dms >/dev/null 2>&1 || :
|
||||
|
||||
# %files
|
||||
# %license LICENSE
|
||||
# %doc README.md CONTRIBUTING.md
|
||||
# %{_datadir}/quickshell/dms/
|
||||
# %{_userunitdir}/dms.service
|
||||
# %{_datadir}/applications/dms-open.desktop
|
||||
# %{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||
|
||||
# %files -n dms-cli
|
||||
# %{_bindir}/dms
|
||||
# %{_datadir}/bash-completion/completions/dms
|
||||
# %{_datadir}/zsh/site-functions/_dms
|
||||
# %{_datadir}/fish/vendor_completions.d/dms.fish
|
||||
|
||||
# %changelog
|
||||
# * CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-1
|
||||
# - Stable release VERSION_PLACEHOLDER
|
||||
# - Built from GitHub release
|
||||
# SPECEOF
|
||||
|
||||
# sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/dms.spec
|
||||
# sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/dms.spec
|
||||
|
||||
# - name: Build SRPM
|
||||
# id: build
|
||||
# run: |
|
||||
# cd ~/rpmbuild/SPECS
|
||||
# rpmbuild -bs dms.spec
|
||||
|
||||
# SRPM=$(ls ~/rpmbuild/SRPMS/*.src.rpm | tail -n 1)
|
||||
# SRPM_NAME=$(basename "$SRPM")
|
||||
|
||||
# echo "srpm_path=$SRPM" >> $GITHUB_OUTPUT
|
||||
# echo "srpm_name=$SRPM_NAME" >> $GITHUB_OUTPUT
|
||||
# echo "SRPM built: $SRPM_NAME"
|
||||
|
||||
# - name: Upload SRPM artifact
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: dms-stable-srpm-${{ steps.version.outputs.version }}
|
||||
# path: ${{ steps.build.outputs.srpm_path }}
|
||||
# retention-days: 90
|
||||
|
||||
# - name: Install Copr CLI
|
||||
# run: |
|
||||
# sudo apt-get install -y python3-pip
|
||||
# pip3 install copr-cli
|
||||
|
||||
# mkdir -p ~/.config
|
||||
# cat > ~/.config/copr << EOF
|
||||
# [copr-cli]
|
||||
# login = ${{ secrets.COPR_LOGIN }}
|
||||
# username = avengemedia
|
||||
# token = ${{ secrets.COPR_TOKEN }}
|
||||
# copr_url = https://copr.fedorainfracloud.org
|
||||
# EOF
|
||||
# chmod 600 ~/.config/copr
|
||||
|
||||
# - name: Upload to Copr
|
||||
# run: |
|
||||
# SRPM="${{ steps.build.outputs.srpm_path }}"
|
||||
# VERSION="${{ steps.version.outputs.version }}"
|
||||
|
||||
# echo "Uploading SRPM to avengemedia/dms..."
|
||||
# BUILD_OUTPUT=$(copr-cli build avengemedia/dms "$SRPM" --nowait 2>&1)
|
||||
# echo "$BUILD_OUTPUT"
|
||||
|
||||
# BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -oP 'Build was added to.*\K[0-9]+' || echo "unknown")
|
||||
|
||||
# if [ "$BUILD_ID" != "unknown" ]; then
|
||||
# echo "Build submitted: https://copr.fedorainfracloud.org/coprs/avengemedia/dms/build/$BUILD_ID/"
|
||||
# fi
|
||||
|
||||
218
.github/workflows/run-copr.yml
vendored
218
.github/workflows/run-copr.yml
vendored
@@ -3,8 +3,17 @@ name: DMS Copr Stable Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package:
|
||||
description: 'Package to build (dms, dms-greeter, or both)'
|
||||
required: false
|
||||
default: 'dms'
|
||||
type: choice
|
||||
options:
|
||||
- dms
|
||||
- dms-greeter
|
||||
- both
|
||||
version:
|
||||
description: 'Versioning (e.g., 0.1.14, leave empty for latest release)'
|
||||
description: 'Versioning (e.g., 1.0.3, leave empty for latest release)'
|
||||
required: false
|
||||
default: ''
|
||||
release:
|
||||
@@ -13,8 +22,27 @@ on:
|
||||
default: '1'
|
||||
|
||||
jobs:
|
||||
build-and-upload:
|
||||
determine-packages:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
packages: ${{ steps.set-packages.outputs.packages }}
|
||||
steps:
|
||||
- name: Set package list
|
||||
id: set-packages
|
||||
run: |
|
||||
PACKAGE_INPUT="${{ github.event.inputs.package || 'dms' }}"
|
||||
if [ "$PACKAGE_INPUT" = "both" ]; then
|
||||
echo 'packages=["dms","dms-greeter"]' >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "packages=[\"$PACKAGE_INPUT\"]" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
build-and-upload:
|
||||
needs: determine-packages
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package: ${{ fromJSON(needs.determine-packages.outputs.packages) }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -39,7 +67,7 @@ jobs:
|
||||
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "release=$RELEASE" >> $GITHUB_OUTPUT
|
||||
echo "✅ Building DMS hotfix version: $VERSION-$RELEASE"
|
||||
echo "✅ Building ${{ matrix.package }} version: $VERSION-$RELEASE"
|
||||
|
||||
- name: Setup build environment
|
||||
run: |
|
||||
@@ -70,157 +98,31 @@ jobs:
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
RELEASE="${{ steps.version.outputs.release }}"
|
||||
CHANGELOG_DATE="$(date '+%a %b %d %Y')"
|
||||
PACKAGE="${{ matrix.package }}"
|
||||
|
||||
cat > ~/rpmbuild/SPECS/dms.spec <<'SPECEOF'
|
||||
# Spec for DMS stable releases - Generated by GitHub Actions
|
||||
# Copy spec file from repository
|
||||
cp distro/fedora/${PACKAGE}.spec ~/rpmbuild/SPECS/${PACKAGE}.spec
|
||||
|
||||
%global debug_package %{nil}
|
||||
%global version VERSION_PLACEHOLDER
|
||||
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||
# Replace placeholders with actual values
|
||||
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/${PACKAGE}.spec
|
||||
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/${PACKAGE}.spec
|
||||
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/${PACKAGE}.spec
|
||||
|
||||
Name: dms
|
||||
Version: %{version}
|
||||
Release: RELEASE_PLACEHOLDER%{?dist}
|
||||
Summary: %{pkg_summary}
|
||||
|
||||
License: MIT
|
||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
|
||||
Source0: dms-qml.tar.gz
|
||||
|
||||
BuildRequires: gzip
|
||||
BuildRequires: wget
|
||||
BuildRequires: systemd-rpm-macros
|
||||
|
||||
Requires: (quickshell or quickshell-git)
|
||||
Requires: accountsservice
|
||||
Requires: dms-cli = %{version}-%{release}
|
||||
Requires: dgop
|
||||
|
||||
Recommends: cava
|
||||
Recommends: cliphist
|
||||
Recommends: danksearch
|
||||
Recommends: hyprpicker
|
||||
Recommends: matugen
|
||||
Recommends: wl-clipboard
|
||||
Recommends: NetworkManager
|
||||
Recommends: qt6-qtmultimedia
|
||||
Suggests: qt6ct
|
||||
|
||||
%description
|
||||
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
||||
and optimized for the niri and hyprland compositors. Features notifications,
|
||||
app launcher, wallpaper customization, and fully customizable with plugins.
|
||||
|
||||
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
|
||||
process monitoring, notification center, clipboard history, dock, control center,
|
||||
lock screen, and comprehensive plugin system.
|
||||
|
||||
%package -n dms-cli
|
||||
Summary: DankMaterialShell CLI tool
|
||||
License: MIT
|
||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
|
||||
%description -n dms-cli
|
||||
Command-line interface for DankMaterialShell configuration and management.
|
||||
Provides native DBus bindings, NetworkManager integration, and system utilities.
|
||||
|
||||
%prep
|
||||
%setup -q -c -n dms-qml
|
||||
|
||||
# Download architecture-specific binaries during build
|
||||
# This ensures the correct architecture is used for each build target
|
||||
case "%{_arch}" in
|
||||
x86_64)
|
||||
ARCH_SUFFIX="amd64"
|
||||
;;
|
||||
aarch64)
|
||||
ARCH_SUFFIX="arm64"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported architecture: %{_arch}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Download dms-cli for target architecture
|
||||
wget -O %{_builddir}/dms-cli.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-${ARCH_SUFFIX}.gz" || {
|
||||
echo "Failed to download dms-cli for architecture %{_arch}"
|
||||
exit 1
|
||||
}
|
||||
gunzip -c %{_builddir}/dms-cli.gz > %{_builddir}/dms-cli
|
||||
chmod +x %{_builddir}/dms-cli
|
||||
|
||||
%build
|
||||
|
||||
%install
|
||||
install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms
|
||||
|
||||
# Shell completions
|
||||
install -d %{buildroot}%{_datadir}/bash-completion/completions
|
||||
install -d %{buildroot}%{_datadir}/zsh/site-functions
|
||||
install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
||||
%{_builddir}/dms-cli completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||
%{_builddir}/dms-cli completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||
%{_builddir}/dms-cli completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||
|
||||
install -Dm644 %{_builddir}/dms-qml/assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
|
||||
|
||||
%posttrans
|
||||
# Clean up old installation path from previous versions (only if empty)
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||
# Remove directories only if empty (preserves any user-added files)
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
fi
|
||||
# Signal running DMS instances to reload (harmless if none running)
|
||||
pkill -USR1 -x dms >/dev/null 2>&1 || :
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc README.md CONTRIBUTING.md
|
||||
%{_datadir}/quickshell/dms/
|
||||
%{_userunitdir}/dms.service
|
||||
|
||||
%files -n dms-cli
|
||||
%{_bindir}/dms
|
||||
%{_datadir}/bash-completion/completions/dms
|
||||
%{_datadir}/zsh/site-functions/_dms
|
||||
%{_datadir}/fish/vendor_completions.d/dms.fish
|
||||
|
||||
%changelog
|
||||
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-RELEASE_PLACEHOLDER
|
||||
- Stable release VERSION_PLACEHOLDER
|
||||
- Built from GitHub release
|
||||
SPECEOF
|
||||
|
||||
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/dms.spec
|
||||
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/dms.spec
|
||||
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/dms.spec
|
||||
|
||||
echo "✅ Spec file generated for v${VERSION}-${RELEASE}"
|
||||
echo "✅ Spec file generated for ${PACKAGE} v${VERSION}-${RELEASE}"
|
||||
echo ""
|
||||
echo "=== Spec file preview ==="
|
||||
head -40 ~/rpmbuild/SPECS/dms.spec
|
||||
head -40 ~/rpmbuild/SPECS/${PACKAGE}.spec
|
||||
|
||||
- name: Build SRPM
|
||||
id: build
|
||||
run: |
|
||||
cd ~/rpmbuild/SPECS
|
||||
PACKAGE="${{ matrix.package }}"
|
||||
|
||||
echo "🔨 Building SRPM..."
|
||||
rpmbuild -bs dms.spec
|
||||
echo "🔨 Building SRPM for ${PACKAGE}..."
|
||||
rpmbuild -bs ${PACKAGE}.spec
|
||||
|
||||
SRPM=$(ls ~/rpmbuild/SRPMS/*.src.rpm | tail -n 1)
|
||||
SRPM=$(ls ~/rpmbuild/SRPMS/${PACKAGE}-*.src.rpm | tail -n 1)
|
||||
SRPM_NAME=$(basename "$SRPM")
|
||||
|
||||
echo "srpm_path=$SRPM" >> $GITHUB_OUTPUT
|
||||
@@ -234,7 +136,7 @@ jobs:
|
||||
- name: Upload SRPM artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dms-stable-srpm-${{ steps.version.outputs.version }}
|
||||
name: ${{ matrix.package }}-stable-srpm-${{ steps.version.outputs.version }}
|
||||
path: ${{ steps.build.outputs.srpm_path }}
|
||||
retention-days: 90
|
||||
|
||||
@@ -255,23 +157,40 @@ jobs:
|
||||
|
||||
echo "✅ Copr CLI configured"
|
||||
|
||||
- name: Determine Copr project
|
||||
id: copr_project
|
||||
run: |
|
||||
PACKAGE="${{ matrix.package }}"
|
||||
if [ "$PACKAGE" = "dms" ]; then
|
||||
COPR_PROJECT="avengemedia/dms"
|
||||
elif [ "$PACKAGE" = "dms-greeter" ]; then
|
||||
COPR_PROJECT="avengemedia/danklinux"
|
||||
else
|
||||
echo "❌ Unknown package: $PACKAGE"
|
||||
exit 1
|
||||
fi
|
||||
echo "copr_project=$COPR_PROJECT" >> $GITHUB_OUTPUT
|
||||
echo "✅ Copr project: $COPR_PROJECT"
|
||||
|
||||
- name: Upload to Copr
|
||||
run: |
|
||||
SRPM="${{ steps.build.outputs.srpm_path }}"
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
COPR_PROJECT="${{ steps.copr_project.outputs.copr_project }}"
|
||||
PACKAGE="${{ matrix.package }}"
|
||||
|
||||
echo "🚀 Uploading SRPM to avengemedia/dms..."
|
||||
echo "🚀 Uploading ${PACKAGE} SRPM to ${COPR_PROJECT}..."
|
||||
echo " SRPM: $(basename $SRPM)"
|
||||
echo " Version: $VERSION"
|
||||
|
||||
BUILD_OUTPUT=$(copr-cli build avengemedia/dms "$SRPM" --nowait 2>&1)
|
||||
BUILD_OUTPUT=$(copr-cli build "$COPR_PROJECT" "$SRPM" --nowait 2>&1)
|
||||
echo "$BUILD_OUTPUT"
|
||||
|
||||
BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -oP 'Build was added to.*\K[0-9]+' || echo "unknown")
|
||||
|
||||
if [ "$BUILD_ID" != "unknown" ]; then
|
||||
echo "✅ Build submitted successfully!"
|
||||
echo "🔗 https://copr.fedorainfracloud.org/coprs/avengemedia/dms/build/$BUILD_ID/"
|
||||
echo "🔗 https://copr.fedorainfracloud.org/coprs/${COPR_PROJECT}/build/$BUILD_ID/"
|
||||
else
|
||||
echo "⚠️ Could not extract build ID, but upload may have succeeded"
|
||||
fi
|
||||
@@ -279,10 +198,13 @@ jobs:
|
||||
- name: Build summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "### 🎉 DMS Stable Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||
PACKAGE="${{ matrix.package }}"
|
||||
COPR_PROJECT="${{ steps.copr_project.outputs.copr_project }}"
|
||||
echo "### 🎉 ${PACKAGE} Stable Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Package:** ${PACKAGE}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Version:** ${{ steps.version.outputs.version }}-${{ steps.version.outputs.release }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **SRPM:** ${{ steps.build.outputs.srpm_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Project:** https://copr.fedorainfracloud.org/coprs/avengemedia/dms/" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Project:** https://copr.fedorainfracloud.org/coprs/${COPR_PROJECT}/" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Stable release has been built and uploaded to Copr!" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
71
.github/workflows/run-obs.yml
vendored
71
.github/workflows/run-obs.yml
vendored
@@ -7,13 +7,14 @@ on:
|
||||
description: "Package to update (dms, dms-git, or all)"
|
||||
required: false
|
||||
default: "all"
|
||||
tag_version:
|
||||
description: "Specific tag version for dms stable (e.g., v1.0.2). Leave empty to auto-detect latest release."
|
||||
required: false
|
||||
default: ""
|
||||
rebuild_release:
|
||||
description: "Release number for rebuilds (e.g., 2, 3, 4 to increment spec Release)"
|
||||
required: false
|
||||
default: ""
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
schedule:
|
||||
- cron: "0 */3 * * *" # Every 3 hours for dms-git builds
|
||||
|
||||
@@ -97,7 +98,7 @@ jobs:
|
||||
# Rebuild requested - always proceed
|
||||
echo "packages=$PKG" >> $GITHUB_OUTPUT
|
||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
||||
echo "🔄 Manual rebuild requested: $PKG (ppa$REBUILD)"
|
||||
echo "🔄 Manual rebuild requested: $PKG (db$REBUILD)"
|
||||
|
||||
elif [[ "$PKG" == "all" ]]; then
|
||||
# Check each package and build list of those needing updates
|
||||
@@ -161,16 +162,51 @@ jobs:
|
||||
id: packages
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
|
||||
# Tag push event - use the pushed tag
|
||||
echo "packages=dms" >> $GITHUB_OUTPUT
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Triggered by tag: $VERSION"
|
||||
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||
# Scheduled run - dms-git only
|
||||
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
||||
echo "Triggered by schedule: updating git package"
|
||||
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
||||
# Use filtered packages from check-updates when package="all" and no rebuild requested
|
||||
if [[ "${{ github.event.inputs.package }}" == "all" ]] && [[ -z "${{ github.event.inputs.rebuild_release }}" ]]; then
|
||||
# Manual workflow dispatch
|
||||
|
||||
# Determine version for dms stable
|
||||
if [[ "${{ github.event.inputs.package }}" == "dms" ]]; then
|
||||
# For explicit dms selection, require tag_version
|
||||
if [[ -n "${{ github.event.inputs.tag_version }}" ]]; then
|
||||
VERSION="${{ github.event.inputs.tag_version }}"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Using specified tag: $VERSION"
|
||||
else
|
||||
echo "ERROR: tag_version is required when package=dms"
|
||||
echo "Please specify a tag version (e.g., v1.0.2) or use package=all for auto-detection"
|
||||
exit 1
|
||||
fi
|
||||
elif [[ "${{ github.event.inputs.package }}" == "all" ]]; then
|
||||
# For "all", auto-detect if tag_version not specified
|
||||
if [[ -n "${{ github.event.inputs.tag_version }}" ]]; then
|
||||
VERSION="${{ github.event.inputs.tag_version }}"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Using specified tag: $VERSION"
|
||||
else
|
||||
# Auto-detect latest release for "all"
|
||||
LATEST_TAG=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | grep '"tag_name"' | sed 's/.*"tag_name": "\([^"]*\)".*/\1/' || echo "")
|
||||
if [[ -n "$LATEST_TAG" ]]; then
|
||||
echo "version=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
echo "Auto-detected latest release: $LATEST_TAG"
|
||||
else
|
||||
echo "ERROR: Could not auto-detect latest release"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Use filtered packages from check-updates when package="all" and no rebuild/tag specified
|
||||
if [[ "${{ github.event.inputs.package }}" == "all" ]] && [[ -z "${{ github.event.inputs.rebuild_release }}" ]] && [[ -z "${{ github.event.inputs.tag_version }}" ]]; then
|
||||
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
||||
echo "Manual trigger: all (filtered to: ${{ needs.check-updates.outputs.packages }})"
|
||||
else
|
||||
@@ -186,7 +222,7 @@ jobs:
|
||||
run: |
|
||||
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.2")
|
||||
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "1.0.2")
|
||||
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
|
||||
echo "📦 Updating dms-git.spec to version: $NEW_VERSION"
|
||||
|
||||
@@ -207,14 +243,14 @@ jobs:
|
||||
run: |
|
||||
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.2")
|
||||
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "1.0.2")
|
||||
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
|
||||
echo "📦 Updating Debian dms-git changelog to version: $NEW_VERSION"
|
||||
|
||||
# Single changelog entry (git snapshots don't need history)
|
||||
CHANGELOG_DATE=$(date -R)
|
||||
{
|
||||
echo "dms-git ($NEW_VERSION) nightly; urgency=medium"
|
||||
echo "dms-git (${NEW_VERSION}db1) nightly; urgency=medium"
|
||||
echo ""
|
||||
echo " * Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)"
|
||||
echo ""
|
||||
@@ -226,10 +262,15 @@ jobs:
|
||||
run: |
|
||||
VERSION="${{ steps.packages.outputs.version }}"
|
||||
VERSION_NO_V="${VERSION#v}"
|
||||
echo "Updating packaging to version $VERSION_NO_V"
|
||||
echo "==> Updating packaging files to version: $VERSION_NO_V"
|
||||
|
||||
# Update spec file
|
||||
sed -i "s/^Version:.*/Version: $VERSION_NO_V/" distro/opensuse/dms.spec
|
||||
|
||||
# Verify the update
|
||||
UPDATED_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1)
|
||||
echo "✓ Spec file now shows Version: $UPDATED_VERSION"
|
||||
|
||||
# Single changelog entry (full history on OBS website)
|
||||
DATE_STR=$(date "+%a %b %d %Y")
|
||||
LOCAL_SPEC_HEAD=$(sed -n '1,/%changelog/{ /%changelog/d; p }' distro/opensuse/dms.spec)
|
||||
@@ -256,13 +297,13 @@ jobs:
|
||||
if [[ -f "distro/debian/dms/debian/changelog" ]]; then
|
||||
CHANGELOG_DATE=$(date -R)
|
||||
{
|
||||
echo "dms ($VERSION_NO_V) stable; urgency=medium"
|
||||
echo "dms (${VERSION_NO_V}db1) stable; urgency=medium"
|
||||
echo ""
|
||||
echo " * Update to $VERSION stable release"
|
||||
echo ""
|
||||
echo " -- Avenge Media <AvengeMedia.US@gmail.com> $CHANGELOG_DATE"
|
||||
} > "distro/debian/dms/debian/changelog"
|
||||
echo "✓ Updated Debian changelog to $VERSION_NO_V"
|
||||
echo "✓ Updated Debian changelog to ${VERSION_NO_V}db1"
|
||||
fi
|
||||
|
||||
- name: Install Go
|
||||
@@ -289,6 +330,7 @@ jobs:
|
||||
- name: Upload to OBS
|
||||
env:
|
||||
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
|
||||
TAG_VERSION: ${{ steps.packages.outputs.version }}
|
||||
run: |
|
||||
PACKAGES="${{ steps.packages.outputs.packages }}"
|
||||
|
||||
@@ -300,6 +342,7 @@ jobs:
|
||||
MESSAGE="Automated update from GitHub Actions"
|
||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||
MESSAGE="Update to ${{ steps.packages.outputs.version }}"
|
||||
echo "==> Version being uploaded: ${{ steps.packages.outputs.version }}"
|
||||
fi
|
||||
|
||||
# PACKAGES can be space-separated list (e.g., "dms-git dms" from "all" check)
|
||||
@@ -309,7 +352,7 @@ jobs:
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Uploading $PKG to OBS..."
|
||||
if [[ -n "$REBUILD_RELEASE" ]]; then
|
||||
echo "🔄 Using rebuild release number: ppa$REBUILD_RELEASE"
|
||||
echo "🔄 Using rebuild release number: db$REBUILD_RELEASE"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
@@ -350,7 +393,7 @@ jobs:
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [[ -n "${{ github.event.inputs.rebuild_release }}" ]]; then
|
||||
echo "**Rebuild Number:** ppa${{ github.event.inputs.rebuild_release }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Rebuild Number:** db${{ github.event.inputs.rebuild_release }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
|
||||
3
.github/workflows/run-ppa.yml
vendored
3
.github/workflows/run-ppa.yml
vendored
@@ -51,7 +51,8 @@ jobs:
|
||||
check_stable_package() {
|
||||
local PKG="$1"
|
||||
local PPA_NAME="$2"
|
||||
local LATEST_TAG=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | grep '"tag_name"' | sed 's/.*"tag_name": "v\?\([^"]*\)".*/\1/' || echo "")
|
||||
# Use git ls-remote to find the latest tag, sorted by version (descending)
|
||||
local LATEST_TAG=$(git ls-remote --tags --refs --sort='-v:refname' https://github.com/AvengeMedia/DankMaterialShell.git | head -n1 | awk -F/ '{print $NF}' | sed 's/^v//')
|
||||
local PPA_VERSION=$(curl -s "https://api.launchpad.net/1.0/~avengemedia/+archive/ubuntu/$PPA_NAME?ws.op=getPublishedSources&source_name=$PKG&status=Published" | grep -oP '"source_package_version":\s*"\K[^"]+' | head -1 || echo "")
|
||||
local PPA_BASE_VERSION=$(echo "$PPA_VERSION" | sed 's/ppa[0-9]*$//')
|
||||
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -96,11 +96,12 @@ go.work
|
||||
go.work.sum
|
||||
|
||||
# env file
|
||||
.env
|
||||
.env*
|
||||
|
||||
# Editor/IDE
|
||||
# .idea/
|
||||
# .vscode/
|
||||
vim/
|
||||
|
||||
bin/
|
||||
|
||||
|
||||
11
CHANGELOG.MD
11
CHANGELOG.MD
@@ -1,6 +1,17 @@
|
||||
This file is more of a quick reference so I know what to account for before next releases.
|
||||
|
||||
# 1.2.0
|
||||
|
||||
- Added clipboard and clipboard history integration
|
||||
- Added swipe to dismiss notification popups and from center
|
||||
- Added paste from clipboard history view - requires wtype
|
||||
- Optimize surface damage of OSD & Toast
|
||||
- Add monitor configurator (niri, Hyprland, MangoWC)
|
||||
- **BREAKING** ghostty theme changed to ~/.config/ghostty/themes/danktheme
|
||||
- requires intervention and doc update
|
||||
- Added desktop widget plugins
|
||||
- dev guidance available
|
||||
- builtin clock & dgop widgets
|
||||
- new IPC targets
|
||||
- Initial RTL support/i18n
|
||||
- Theme registry
|
||||
|
||||
@@ -163,7 +163,7 @@ quickshell -p quickshell/
|
||||
inputs.dms.url = "github:AvengeMedia/DankMaterialShell";
|
||||
|
||||
# Use in home-manager or NixOS configuration
|
||||
imports = [ inputs.dms.homeModules.dankMaterialShell.default ];
|
||||
imports = [ inputs.dms.homeModules.dank-material-shell ];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=DMS Application Picker
|
||||
Name=DMS
|
||||
Comment=Select an application to open links and files
|
||||
Exec=dms open %u
|
||||
Icon=danklogo
|
||||
Terminal=false
|
||||
NoDisplay=true
|
||||
MimeType=x-scheme-handler/http;x-scheme-handler/https;text/html;application/xhtml+xml;
|
||||
MimeType=x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/dms;text/html;application/xhtml+xml;
|
||||
Categories=Utility;
|
||||
|
||||
@@ -9,7 +9,7 @@ Type=dbus
|
||||
BusName=org.freedesktop.Notifications
|
||||
ExecStart=/usr/bin/dms run --session
|
||||
ExecReload=/usr/bin/pkill -USR1 -x dms
|
||||
Restart=always
|
||||
Restart=on-failure
|
||||
RestartSec=1.23
|
||||
TimeoutStopSec=10
|
||||
|
||||
|
||||
@@ -14,34 +14,63 @@ Distribution-aware installer with TUI for deploying DMS and compositor configura
|
||||
|
||||
## System Integration
|
||||
|
||||
**Wayland Protocols**
|
||||
- `wlr-gamma-control-unstable-v1` - Night mode and gamma control
|
||||
- `wlr-screencopy-unstable-v1` - Screen capture for color picker
|
||||
- `wlr-layer-shell-unstable-v1` - Overlay surfaces for color picker
|
||||
- `wp-viewporter` - Fractional scaling support
|
||||
- `dwl-ipc-unstable-v2` - dwl/MangoWC workspace integration
|
||||
- `ext-workspace-v1` - Workspace protocol support
|
||||
- `wlr-output-management-unstable-v1` - Display configuration
|
||||
### Wayland Protocols (Client)
|
||||
|
||||
**DBus Interfaces**
|
||||
- NetworkManager/iwd - Network management
|
||||
- logind - Session control and inhibit locks
|
||||
- accountsservice - User account information
|
||||
- CUPS - Printer management
|
||||
- Custom IPC via unix socket (JSON API)
|
||||
All Wayland protocols are consumed as a client - connecting to the compositor.
|
||||
|
||||
**Hardware Control**
|
||||
- DDC/CI protocol - External monitor brightness control (like `ddcutil`)
|
||||
- Backlight control - Internal display brightness via `login1` or sysfs
|
||||
- LED control - Keyboard/device LED management
|
||||
- evdev input monitoring - Keyboard state tracking (caps lock, etc.)
|
||||
| Protocol | Purpose |
|
||||
| ----------------------------------------- | ----------------------------------------------------------- |
|
||||
| `wlr-gamma-control-unstable-v1` | Night mode color temperature control |
|
||||
| `wlr-screencopy-unstable-v1` | Screen capture for color picker/screenshot |
|
||||
| `wlr-layer-shell-unstable-v1` | Overlay surfaces for color picker UI/screenshot |
|
||||
| `wlr-output-management-unstable-v1` | Display configuration |
|
||||
| `wlr-output-power-management-unstable-v1` | DPMS on/off CLI |
|
||||
| `wp-viewporter` | Fractional scaling support (color picker/screenshot UIs) |
|
||||
| `keyboard-shortcuts-inhibit-unstable-v1` | Inhibit compositor shortcuts during color picker/screenshot |
|
||||
| `ext-data-control-v1` | Clipboard history and persistence |
|
||||
| `ext-workspace-v1` | Workspace integration |
|
||||
| `dwl-ipc-unstable-v2` | dwl/MangoWC IPC for tags, outputs, etc. |
|
||||
|
||||
### DBus Interfaces
|
||||
|
||||
**Client (consuming external services):**
|
||||
|
||||
| Interface | Purpose |
|
||||
| -------------------------------- | --------------------------------------------- |
|
||||
| `org.bluez` | Bluetooth management with pairing agent |
|
||||
| `org.freedesktop.NetworkManager` | Network management |
|
||||
| `net.connman.iwd` | iwd Wi-Fi backend |
|
||||
| `org.freedesktop.network1` | systemd-networkd integration |
|
||||
| `org.freedesktop.login1` | Session control, sleep inhibitors, brightness |
|
||||
| `org.freedesktop.Accounts` | User account information |
|
||||
| `org.freedesktop.portal.Desktop` | Desktop appearance settings (color scheme) |
|
||||
| CUPS via IPP + D-Bus | Printer management with job notifications |
|
||||
|
||||
**Server (implementing interfaces):**
|
||||
|
||||
| Interface | Purpose |
|
||||
| ----------------------------- | -------------------------------------- |
|
||||
| `org.freedesktop.ScreenSaver` | Screensaver inhibit for video playback |
|
||||
|
||||
Custom IPC via unix socket (JSON API) for shell communication.
|
||||
|
||||
### Hardware Control
|
||||
|
||||
| Subsystem | Method | Purpose |
|
||||
| --------- | ------------------- | ---------------------------------- |
|
||||
| DDC/CI | I2C direct | External monitor brightness |
|
||||
| Backlight | logind or sysfs | Internal display brightness |
|
||||
| evdev | `/dev/input/event*` | Keyboard state (caps lock LED) |
|
||||
| udev | netlink monitor | Backlight device updates (for OSD) |
|
||||
|
||||
### Plugin System
|
||||
|
||||
**Plugin System**
|
||||
- Plugin registry integration
|
||||
- Plugin lifecycle management
|
||||
- Settings persistence
|
||||
|
||||
## CLI Commands
|
||||
|
||||
- `dms run [-d]` - Start shell (optionally as daemon)
|
||||
- `dms restart` / `dms kill` - Manage running processes
|
||||
- `dms ipc <command>` - Send IPC commands (toggle launcher, notifications, etc.)
|
||||
@@ -70,6 +99,7 @@ The on-screen preview displays the selected format. JSON output includes hex, RG
|
||||
Requires Go 1.24+
|
||||
|
||||
**Development build:**
|
||||
|
||||
```bash
|
||||
make # Build dms CLI
|
||||
make dankinstall # Build installer
|
||||
@@ -77,6 +107,7 @@ make test # Run tests
|
||||
```
|
||||
|
||||
**Distribution build:**
|
||||
|
||||
```bash
|
||||
make dist # Build without update/greeter features
|
||||
```
|
||||
@@ -84,6 +115,7 @@ make dist # Build without update/greeter features
|
||||
Produces `bin/dms-linux-amd64` and `bin/dms-linux-arm64`
|
||||
|
||||
**Installation:**
|
||||
|
||||
```bash
|
||||
sudo make install # Install to /usr/local/bin/dms
|
||||
```
|
||||
@@ -91,6 +123,7 @@ sudo make install # Install to /usr/local/bin/dms
|
||||
## Development
|
||||
|
||||
**Setup pre-commit hooks:**
|
||||
|
||||
```bash
|
||||
git config core.hooksPath .githooks
|
||||
```
|
||||
@@ -98,6 +131,7 @@ git config core.hooksPath .githooks
|
||||
This runs gofmt, golangci-lint, tests, and builds before each commit when `core/` files are staged.
|
||||
|
||||
**Regenerating Wayland Protocol Bindings:**
|
||||
|
||||
```bash
|
||||
go install github.com/rajveermalviya/go-wayland/cmd/go-wayland-scanner@latest
|
||||
go-wayland-scanner -i internal/proto/xml/wlr-gamma-control-unstable-v1.xml \
|
||||
@@ -105,6 +139,7 @@ go-wayland-scanner -i internal/proto/xml/wlr-gamma-control-unstable-v1.xml \
|
||||
```
|
||||
|
||||
**Module Structure:**
|
||||
|
||||
- `cmd/` - Binary entrypoints (dms, dankinstall)
|
||||
- `internal/distros/` - Distribution-specific installation logic
|
||||
- `internal/proto/` - Wayland protocol bindings
|
||||
|
||||
@@ -144,8 +144,6 @@ var (
|
||||
clipConfigEnabled bool
|
||||
clipConfigDisableHistory bool
|
||||
clipConfigEnableHistory bool
|
||||
clipConfigDisablePersist bool
|
||||
clipConfigEnablePersist bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -173,8 +171,6 @@ func init() {
|
||||
clipConfigSetCmd.Flags().BoolVar(&clipConfigEnabled, "enable", false, "Enable clipboard manager")
|
||||
clipConfigSetCmd.Flags().BoolVar(&clipConfigDisableHistory, "disable-history", false, "Disable clipboard history persistence")
|
||||
clipConfigSetCmd.Flags().BoolVar(&clipConfigEnableHistory, "enable-history", false, "Enable clipboard history persistence")
|
||||
clipConfigSetCmd.Flags().BoolVar(&clipConfigDisablePersist, "disable-persist", false, "Disable clipboard ownership persistence")
|
||||
clipConfigSetCmd.Flags().BoolVar(&clipConfigEnablePersist, "enable-persist", false, "Enable clipboard ownership persistence")
|
||||
|
||||
clipWatchCmd.Flags().BoolVarP(&clipWatchStore, "store", "s", false, "Store clipboard changes to history (no server required)")
|
||||
|
||||
@@ -597,12 +593,6 @@ func runClipConfigSet(cmd *cobra.Command, args []string) {
|
||||
if clipConfigEnableHistory {
|
||||
params["disableHistory"] = false
|
||||
}
|
||||
if clipConfigDisablePersist {
|
||||
params["disablePersist"] = true
|
||||
}
|
||||
if clipConfigEnablePersist {
|
||||
params["disablePersist"] = false
|
||||
}
|
||||
|
||||
if len(params) == 0 {
|
||||
fmt.Println("No config options specified")
|
||||
|
||||
@@ -171,7 +171,6 @@ var pluginsUpdateCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func runVersion(cmd *cobra.Command, args []string) {
|
||||
printASCII()
|
||||
fmt.Printf("%s\n", formatVersion(Version))
|
||||
}
|
||||
|
||||
@@ -220,7 +219,7 @@ func getBaseVersion() string {
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return "0.6.2"
|
||||
return "1.0.2"
|
||||
}
|
||||
|
||||
func startDebugServer() error {
|
||||
|
||||
@@ -22,6 +22,7 @@ func init() {
|
||||
dank16Cmd.Flags().Bool("json", false, "Output in JSON format")
|
||||
dank16Cmd.Flags().Bool("kitty", false, "Output in Kitty terminal format")
|
||||
dank16Cmd.Flags().Bool("foot", false, "Output in Foot terminal format")
|
||||
dank16Cmd.Flags().Bool("neovim", false, "Output in Neovim plugin format")
|
||||
dank16Cmd.Flags().Bool("alacritty", false, "Output in Alacritty terminal format")
|
||||
dank16Cmd.Flags().Bool("ghostty", false, "Output in Ghostty terminal format")
|
||||
dank16Cmd.Flags().Bool("wezterm", false, "Output in Wezterm terminal format")
|
||||
@@ -40,6 +41,7 @@ func runDank16(cmd *cobra.Command, args []string) {
|
||||
isJson, _ := cmd.Flags().GetBool("json")
|
||||
isKitty, _ := cmd.Flags().GetBool("kitty")
|
||||
isFoot, _ := cmd.Flags().GetBool("foot")
|
||||
isNeovim, _ := cmd.Flags().GetBool("neovim")
|
||||
isAlacritty, _ := cmd.Flags().GetBool("alacritty")
|
||||
isGhostty, _ := cmd.Flags().GetBool("ghostty")
|
||||
isWezterm, _ := cmd.Flags().GetBool("wezterm")
|
||||
@@ -116,6 +118,8 @@ func runDank16(cmd *cobra.Command, args []string) {
|
||||
fmt.Print(dank16.GenerateGhosttyTheme(colors))
|
||||
} else if isWezterm {
|
||||
fmt.Print(dank16.GenerateWeztermTheme(colors))
|
||||
} else if isNeovim {
|
||||
fmt.Print(dank16.GenerateNeovimTheme(colors))
|
||||
} else {
|
||||
fmt.Print(dank16.GenerateGhosttyTheme(colors))
|
||||
}
|
||||
|
||||
@@ -131,6 +131,12 @@ func runOpen(target string) {
|
||||
detectedRequestType = "url"
|
||||
}
|
||||
log.Infof("Detected HTTP(S) URL")
|
||||
} else if strings.HasPrefix(target, "dms://") {
|
||||
// Handle DMS internal URLs (theme/plugin install, etc.)
|
||||
if detectedRequestType == "" {
|
||||
detectedRequestType = "url"
|
||||
}
|
||||
log.Infof("Detected DMS internal URL")
|
||||
} else if _, err := os.Stat(target); err == nil {
|
||||
// Handle local file paths directly (not file:// URIs)
|
||||
// Convert to absolute path
|
||||
@@ -177,7 +183,7 @@ func runOpen(target string) {
|
||||
}
|
||||
|
||||
method := "apppicker.open"
|
||||
if detectedMimeType == "" && len(detectedCategories) == 0 && (strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://")) {
|
||||
if detectedMimeType == "" && len(detectedCategories) == 0 && (strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") || strings.HasPrefix(target, "dms://")) {
|
||||
method = "browser.open"
|
||||
params["url"] = target
|
||||
}
|
||||
|
||||
@@ -18,6 +18,25 @@ import (
|
||||
|
||||
type ipcTargets map[string]map[string][]string
|
||||
|
||||
// getProcessExitCode returns the exit code from a ProcessState.
|
||||
// For normal exits, returns the exit code directly.
|
||||
// For signal termination, returns 128 + signal number (Unix convention).
|
||||
func getProcessExitCode(state *os.ProcessState) int {
|
||||
if state == nil {
|
||||
return 1
|
||||
}
|
||||
if code := state.ExitCode(); code != -1 {
|
||||
return code
|
||||
}
|
||||
// Process was killed by signal - extract signal number
|
||||
if status, ok := state.Sys().(syscall.WaitStatus); ok {
|
||||
if status.Signaled() {
|
||||
return 128 + int(status.Signal())
|
||||
}
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
var isSessionManaged bool
|
||||
|
||||
func execDetachedRestart(targetPID int) {
|
||||
@@ -180,6 +199,16 @@ func runShellInteractive(session bool) {
|
||||
}
|
||||
}
|
||||
|
||||
if os.Getenv("QT_QPA_PLATFORMTHEME") == "" {
|
||||
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME=gtk3")
|
||||
}
|
||||
if os.Getenv("QT_QPA_PLATFORMTHEME_QT6") == "" {
|
||||
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME_QT6=gtk3")
|
||||
}
|
||||
if os.Getenv("QT_QPA_PLATFORM") == "" {
|
||||
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland")
|
||||
}
|
||||
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
@@ -214,14 +243,28 @@ func runShellInteractive(session bool) {
|
||||
for {
|
||||
select {
|
||||
case sig := <-sigChan:
|
||||
// Handle SIGUSR1 restart for non-session managed processes
|
||||
if sig == syscall.SIGUSR1 && !isSessionManaged {
|
||||
if sig == syscall.SIGUSR1 {
|
||||
if isSessionManaged {
|
||||
log.Infof("Received SIGUSR1, exiting for systemd restart...")
|
||||
cancel()
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
os.Remove(socketPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Infof("Received SIGUSR1, spawning detached restart process...")
|
||||
execDetachedRestart(os.Getpid())
|
||||
// Exit immediately to avoid race conditions with detached restart
|
||||
return
|
||||
}
|
||||
|
||||
// Check if qs already crashed before we got SIGTERM (systemd sends SIGTERM when D-Bus name is released)
|
||||
select {
|
||||
case <-errChan:
|
||||
cancel()
|
||||
os.Remove(socketPath)
|
||||
os.Exit(getProcessExitCode(cmd.ProcessState))
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
}
|
||||
|
||||
log.Infof("\nReceived signal %v, shutting down...", sig)
|
||||
cancel()
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
@@ -235,7 +278,7 @@ func runShellInteractive(session bool) {
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
}
|
||||
os.Remove(socketPath)
|
||||
os.Exit(1)
|
||||
os.Exit(getProcessExitCode(cmd.ProcessState))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,6 +443,16 @@ func runShellDaemon(session bool) {
|
||||
}
|
||||
}
|
||||
|
||||
if os.Getenv("QT_QPA_PLATFORMTHEME") == "" {
|
||||
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME=gtk3")
|
||||
}
|
||||
if os.Getenv("QT_QPA_PLATFORMTHEME_QT6") == "" {
|
||||
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME_QT6=gtk3")
|
||||
}
|
||||
if os.Getenv("QT_QPA_PLATFORM") == "" {
|
||||
cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland")
|
||||
}
|
||||
|
||||
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
log.Fatalf("Error opening /dev/null: %v", err)
|
||||
@@ -440,15 +493,28 @@ func runShellDaemon(session bool) {
|
||||
for {
|
||||
select {
|
||||
case sig := <-sigChan:
|
||||
// Handle SIGUSR1 restart for non-session managed processes
|
||||
if sig == syscall.SIGUSR1 && !isSessionManaged {
|
||||
if sig == syscall.SIGUSR1 {
|
||||
if isSessionManaged {
|
||||
log.Infof("Received SIGUSR1, exiting for systemd restart...")
|
||||
cancel()
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
os.Remove(socketPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Infof("Received SIGUSR1, spawning detached restart process...")
|
||||
execDetachedRestart(os.Getpid())
|
||||
// Exit immediately to avoid race conditions with detached restart
|
||||
return
|
||||
}
|
||||
|
||||
// All other signals: clean shutdown
|
||||
// Check if qs already crashed before we got SIGTERM (systemd sends SIGTERM when D-Bus name is released)
|
||||
select {
|
||||
case <-errChan:
|
||||
cancel()
|
||||
os.Remove(socketPath)
|
||||
os.Exit(getProcessExitCode(cmd.ProcessState))
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
}
|
||||
|
||||
cancel()
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
os.Remove(socketPath)
|
||||
@@ -460,7 +526,7 @@ func runShellDaemon(session bool) {
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
}
|
||||
os.Remove(socketPath)
|
||||
os.Exit(1)
|
||||
os.Exit(getProcessExitCode(cmd.ProcessState))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,11 @@ func (cd *ConfigDeployer) deployNiriDmsConfigs(dmsDir, terminalCommand string) e
|
||||
|
||||
for _, cfg := range configs {
|
||||
path := filepath.Join(dmsDir, cfg.name)
|
||||
// Skip if file already exists to preserve user modifications
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
cd.log(fmt.Sprintf("Skipping %s (already exists)", cfg.name))
|
||||
continue
|
||||
}
|
||||
if err := os.WriteFile(path, []byte(cfg.content), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write %s: %w", cfg.name, err)
|
||||
}
|
||||
@@ -265,7 +270,13 @@ func (cd *ConfigDeployer) deployGhosttyConfig() ([]DeploymentResult, error) {
|
||||
|
||||
colorResult := DeploymentResult{
|
||||
ConfigType: "Ghostty Colors",
|
||||
Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config-dankcolors"),
|
||||
Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "themes", "dankcolors"),
|
||||
}
|
||||
|
||||
themesDir := filepath.Dir(colorResult.Path)
|
||||
if err := os.MkdirAll(themesDir, 0755); err != nil {
|
||||
mainResult.Error = fmt.Errorf("failed to create themes directory: %w", err)
|
||||
return []DeploymentResult{mainResult}, mainResult.Error
|
||||
}
|
||||
|
||||
if err := os.WriteFile(colorResult.Path, []byte(GhosttyColorConfig), 0644); err != nil {
|
||||
|
||||
@@ -468,7 +468,7 @@ func TestHyprlandConfigStructure(t *testing.T) {
|
||||
func TestGhosttyConfigStructure(t *testing.T) {
|
||||
assert.Contains(t, GhosttyConfig, "window-decoration = false")
|
||||
assert.Contains(t, GhosttyConfig, "background-opacity = 1.0")
|
||||
assert.Contains(t, GhosttyConfig, "config-file = ./config-dankcolors")
|
||||
assert.Contains(t, GhosttyConfig, "theme = dankcolors")
|
||||
}
|
||||
|
||||
func TestGhosttyColorConfigStructure(t *testing.T) {
|
||||
|
||||
@@ -48,4 +48,4 @@ keybind = shift+enter=text:\n
|
||||
gtk-single-instance = true
|
||||
|
||||
# Dank color generation
|
||||
config-file = ./config-dankcolors
|
||||
theme = dankcolors
|
||||
|
||||
@@ -112,3 +112,24 @@ func GenerateWeztermTheme(p Palette) string {
|
||||
p.Color12.Hex, p.Color13.Hex, p.Color14.Hex, p.Color15.Hex)
|
||||
return result.String()
|
||||
}
|
||||
|
||||
func GenerateNeovimTheme(p Palette) string {
|
||||
var result strings.Builder
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_0 = \"%s\"\n", p.Color0.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_1 = \"%s\"\n", p.Color1.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_2 = \"%s\"\n", p.Color2.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_3 = \"%s\"\n", p.Color3.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_4 = \"%s\"\n", p.Color4.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_5 = \"%s\"\n", p.Color5.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_6 = \"%s\"\n", p.Color6.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_7 = \"%s\"\n", p.Color7.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_8 = \"%s\"\n", p.Color8.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_9 = \"%s\"\n", p.Color9.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_10 = \"%s\"\n", p.Color10.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_11 = \"%s\"\n", p.Color11.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_12 = \"%s\"\n", p.Color12.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_13 = \"%s\"\n", p.Color13.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_14 = \"%s\"\n", p.Color14.Hex)
|
||||
fmt.Fprintf(&result, "vim.g.terminal_color_15 = \"%s\"\n", p.Color15.Hex)
|
||||
return result.String()
|
||||
}
|
||||
|
||||
@@ -550,10 +550,7 @@ func (b *BaseDistribution) WriteEnvironmentConfig(terminal deps.Terminal) error
|
||||
terminalCmd = "ghostty"
|
||||
}
|
||||
|
||||
content := fmt.Sprintf(`QT_QPA_PLATFORM=wayland
|
||||
ELECTRON_OZONE_PLATFORM_HINT=auto
|
||||
QT_QPA_PLATFORMTHEME=gtk3
|
||||
QT_QPA_PLATFORMTHEME_QT6=gtk3
|
||||
content := fmt.Sprintf(`ELECTRON_OZONE_PLATFORM_HINT=auto
|
||||
TERMINAL=%s
|
||||
`, terminalCmd)
|
||||
|
||||
@@ -567,12 +564,6 @@ TERMINAL=%s
|
||||
}
|
||||
|
||||
func (b *BaseDistribution) EnableDMSService(ctx context.Context, wm deps.WindowManager) error {
|
||||
cmd := exec.CommandContext(ctx, "systemctl", "--user", "enable", "--now", "dms")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to enable dms service: %w", err)
|
||||
}
|
||||
b.log("Enabled dms systemd user service")
|
||||
|
||||
switch wm {
|
||||
case deps.WindowManagerNiri:
|
||||
if err := exec.CommandContext(ctx, "systemctl", "--user", "add-wants", "niri.service", "dms").Run(); err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
||||
@@ -384,6 +385,8 @@ func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Packa
|
||||
debianVersion := "Debian_13"
|
||||
if osInfo.VersionID == "testing" {
|
||||
debianVersion = "Debian_Testing"
|
||||
} else if osInfo.VersionCodename == "sid" || osInfo.VersionID == "sid" || strings.Contains(strings.ToLower(osInfo.PrettyName), "sid") || strings.Contains(strings.ToLower(osInfo.PrettyName), "unstable") {
|
||||
debianVersion = "Debian_Unstable"
|
||||
}
|
||||
|
||||
for _, pkg := range obsPkgs {
|
||||
@@ -427,7 +430,7 @@ func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Packa
|
||||
}
|
||||
|
||||
// Add repository
|
||||
repoLine := fmt.Sprintf("deb [signed-by=%s] %s/ /", keyringPath, baseURL)
|
||||
repoLine := fmt.Sprintf("deb [signed-by=%s, arch=%s] %s/ /", keyringPath, runtime.GOARCH, baseURL)
|
||||
|
||||
progressChan <- InstallProgressMsg{
|
||||
Phase: PhaseSystemPackages,
|
||||
|
||||
@@ -15,6 +15,12 @@ func init() {
|
||||
Register("opensuse-tumbleweed", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewOpenSUSEDistribution(config, logChan)
|
||||
})
|
||||
Register("opensuse-leap", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewOpenSUSEDistribution(config, logChan)
|
||||
})
|
||||
Register("opensuse-slowroll", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewOpenSUSEDistribution(config, logChan)
|
||||
})
|
||||
}
|
||||
|
||||
type OpenSUSEDistribution struct {
|
||||
@@ -434,6 +440,19 @@ func (o *OpenSUSEDistribution) extractPackageNames(packages []PackageMapping) []
|
||||
func (o *OpenSUSEDistribution) enableOBSRepos(ctx context.Context, obsPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
|
||||
enabledRepos := make(map[string]bool)
|
||||
|
||||
osInfo, err := GetOSInfo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get OS info: %w", err)
|
||||
}
|
||||
|
||||
obsDistroVersion := "openSUSE_Tumbleweed"
|
||||
switch osInfo.Distribution.ID {
|
||||
case "opensuse-leap":
|
||||
obsDistroVersion = fmt.Sprintf("openSUSE_Leap_%s", osInfo.VersionID)
|
||||
case "opensuse-slowroll":
|
||||
obsDistroVersion = "openSUSE_Slowroll"
|
||||
}
|
||||
|
||||
for _, pkg := range obsPkgs {
|
||||
if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] {
|
||||
o.log(fmt.Sprintf("Enabling OBS repository: %s", pkg.RepoURL))
|
||||
@@ -441,8 +460,8 @@ func (o *OpenSUSEDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Pac
|
||||
// RepoURL format: "home:AvengeMedia:danklinux"
|
||||
repoPath := strings.ReplaceAll(pkg.RepoURL, ":", ":/")
|
||||
repoName := strings.ReplaceAll(pkg.RepoURL, ":", "-")
|
||||
repoURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/openSUSE_Tumbleweed/%s.repo",
|
||||
repoPath, pkg.RepoURL)
|
||||
repoURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/%s/%s.repo",
|
||||
repoPath, obsDistroVersion, pkg.RepoURL)
|
||||
|
||||
checkCmd := exec.CommandContext(ctx, "zypper", "repos", repoName)
|
||||
if checkCmd.Run() == nil {
|
||||
|
||||
@@ -19,11 +19,12 @@ type DistroInfo struct {
|
||||
|
||||
// OSInfo contains complete OS information
|
||||
type OSInfo struct {
|
||||
Distribution DistroInfo
|
||||
Version string
|
||||
VersionID string
|
||||
PrettyName string
|
||||
Architecture string
|
||||
Distribution DistroInfo
|
||||
Version string
|
||||
VersionID string
|
||||
VersionCodename string
|
||||
PrettyName string
|
||||
Architecture string
|
||||
}
|
||||
|
||||
// GetOSInfo detects the current OS and returns information about it
|
||||
@@ -72,6 +73,8 @@ func GetOSInfo() (*OSInfo, error) {
|
||||
info.VersionID = value
|
||||
case "VERSION":
|
||||
info.Version = value
|
||||
case "VERSION_CODENAME":
|
||||
info.VersionCodename = value
|
||||
case "PRETTY_NAME":
|
||||
info.PrettyName = value
|
||||
}
|
||||
@@ -100,6 +103,10 @@ func IsUnsupportedDistro(distroID, versionID string) bool {
|
||||
}
|
||||
|
||||
if distroID == "debian" {
|
||||
// unstable/sid support
|
||||
if versionID == "sid" {
|
||||
return false
|
||||
}
|
||||
if versionID == "" {
|
||||
// debian testing/sid have no version ID
|
||||
return false
|
||||
|
||||
@@ -258,7 +258,9 @@ output_path = '%s'
|
||||
if !opts.ShouldSkipTemplate("vesktop") {
|
||||
appendConfig(opts, cfgFile, "vesktop", "vesktop.toml")
|
||||
}
|
||||
|
||||
if !opts.ShouldSkipTemplate("equibop") {
|
||||
appendConfig(opts, cfgFile, "equibop", "equibop.toml")
|
||||
}
|
||||
if !opts.ShouldSkipTemplate("ghostty") {
|
||||
appendTerminalConfig(opts, cfgFile, tmpDir, "ghostty", "ghostty.toml")
|
||||
}
|
||||
@@ -274,6 +276,9 @@ output_path = '%s'
|
||||
if !opts.ShouldSkipTemplate("wezterm") {
|
||||
appendTerminalConfig(opts, cfgFile, tmpDir, "wezterm", "wezterm.toml")
|
||||
}
|
||||
if !opts.ShouldSkipTemplate("nvim") {
|
||||
appendTerminalConfig(opts, cfgFile, tmpDir, "nvim", "neovim.toml")
|
||||
}
|
||||
|
||||
if !opts.ShouldSkipTemplate("dgop") {
|
||||
appendConfig(opts, cfgFile, "dgop", "dgop.toml")
|
||||
|
||||
@@ -238,7 +238,7 @@ func (i *ZwlrOutputManagerV1) Dispatch(opcode uint32, fd int, data []byte) {
|
||||
l := 0
|
||||
objectID := client.Uint32(data[l : l+4])
|
||||
proxy := i.Context().GetProxy(objectID)
|
||||
if proxy == nil {
|
||||
if proxy == nil || proxy.IsZombie() {
|
||||
head := &ZwlrOutputHeadV1{}
|
||||
head.SetContext(i.Context())
|
||||
head.SetID(objectID)
|
||||
@@ -723,7 +723,7 @@ func (i *ZwlrOutputHeadV1) Dispatch(opcode uint32, fd int, data []byte) {
|
||||
l := 0
|
||||
objectID := client.Uint32(data[l : l+4])
|
||||
proxy := i.Context().GetProxy(objectID)
|
||||
if proxy == nil {
|
||||
if proxy == nil || proxy.IsZombie() {
|
||||
mode := &ZwlrOutputModeV1{}
|
||||
mode.SetContext(i.Context())
|
||||
mode.SetID(objectID)
|
||||
@@ -761,8 +761,8 @@ func (i *ZwlrOutputHeadV1) Dispatch(opcode uint32, fd int, data []byte) {
|
||||
l := 0
|
||||
objectID := client.Uint32(data[l : l+4])
|
||||
proxy := i.Context().GetProxy(objectID)
|
||||
if proxy == nil {
|
||||
// Mode not yet registered, create it
|
||||
if proxy == nil || proxy.IsZombie() {
|
||||
// Mode not yet registered or zombie, create fresh
|
||||
mode := &ZwlrOutputModeV1{}
|
||||
mode.SetContext(i.Context())
|
||||
mode.SetID(objectID)
|
||||
|
||||
@@ -208,9 +208,6 @@ func handleSetConfig(conn net.Conn, req models.Request, m *Manager) {
|
||||
if v, ok := req.Params["disableHistory"].(bool); ok {
|
||||
cfg.DisableHistory = v
|
||||
}
|
||||
if v, ok := req.Params["disablePersist"].(bool); ok {
|
||||
cfg.DisablePersist = v
|
||||
}
|
||||
|
||||
if err := m.SetConfig(cfg); err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
|
||||
@@ -319,10 +319,6 @@ func (m *Manager) readAndStore(r *os.File, mimeType string) {
|
||||
m.storeClipboardEntry(data, mimeType)
|
||||
}
|
||||
|
||||
if !cfg.DisablePersist {
|
||||
m.persistClipboard([]string{mimeType}, map[string][]byte{mimeType: data})
|
||||
}
|
||||
|
||||
m.updateState()
|
||||
m.notifySubscribers()
|
||||
}
|
||||
@@ -348,105 +344,6 @@ func (m *Manager) storeClipboardEntry(data []byte, mimeType string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) persistClipboard(mimeTypes []string, data map[string][]byte) {
|
||||
m.persistMutex.Lock()
|
||||
m.persistMimeTypes = mimeTypes
|
||||
m.persistData = data
|
||||
m.persistMutex.Unlock()
|
||||
|
||||
m.post(func() {
|
||||
m.takePersistOwnership()
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Manager) takePersistOwnership() {
|
||||
if m.dataControlMgr == nil || m.dataDevice == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if m.getConfig().DisablePersist {
|
||||
return
|
||||
}
|
||||
|
||||
m.persistMutex.RLock()
|
||||
mimeTypes := m.persistMimeTypes
|
||||
m.persistMutex.RUnlock()
|
||||
|
||||
if len(mimeTypes) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
dataMgr := m.dataControlMgr.(*ext_data_control.ExtDataControlManagerV1)
|
||||
|
||||
source, err := dataMgr.CreateDataSource()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create persist source: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, mime := range mimeTypes {
|
||||
if err := source.Offer(mime); err != nil {
|
||||
log.Errorf("Failed to offer mime type %s: %v", mime, err)
|
||||
}
|
||||
}
|
||||
|
||||
source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) {
|
||||
fd := e.Fd
|
||||
defer syscall.Close(fd)
|
||||
|
||||
m.persistMutex.RLock()
|
||||
d := m.persistData[e.MimeType]
|
||||
m.persistMutex.RUnlock()
|
||||
|
||||
if len(d) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
file := os.NewFile(uintptr(fd), "clipboard-pipe")
|
||||
defer file.Close()
|
||||
file.Write(d)
|
||||
})
|
||||
|
||||
source.SetCancelledHandler(func(e ext_data_control.ExtDataControlSourceV1CancelledEvent) {
|
||||
m.ownerLock.Lock()
|
||||
m.isOwner = false
|
||||
m.ownerLock.Unlock()
|
||||
})
|
||||
|
||||
if m.currentSource != nil {
|
||||
oldSource := m.currentSource.(*ext_data_control.ExtDataControlSourceV1)
|
||||
oldSource.Destroy()
|
||||
}
|
||||
m.currentSource = source
|
||||
|
||||
device := m.dataDevice.(*ext_data_control.ExtDataControlDeviceV1)
|
||||
if err := device.SetSelection(source); err != nil {
|
||||
log.Errorf("Failed to set persist selection: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
m.ownerLock.Lock()
|
||||
m.isOwner = true
|
||||
m.ownerLock.Unlock()
|
||||
}
|
||||
|
||||
func (m *Manager) releaseOwnership() {
|
||||
m.ownerLock.Lock()
|
||||
m.isOwner = false
|
||||
m.ownerLock.Unlock()
|
||||
|
||||
m.persistMutex.Lock()
|
||||
m.persistData = nil
|
||||
m.persistMimeTypes = nil
|
||||
m.persistMutex.Unlock()
|
||||
|
||||
if m.currentSource != nil {
|
||||
source := m.currentSource.(*ext_data_control.ExtDataControlSourceV1)
|
||||
source.Destroy()
|
||||
m.currentSource = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) storeEntry(entry Entry) error {
|
||||
if m.db == nil {
|
||||
return fmt.Errorf("database not available")
|
||||
@@ -495,6 +392,9 @@ func (m *Manager) deduplicateInTx(b *bolt.Bucket, hash uint64) error {
|
||||
}
|
||||
|
||||
func (m *Manager) trimLengthInTx(b *bolt.Bucket) error {
|
||||
if m.config.MaxHistory < 0 {
|
||||
return nil
|
||||
}
|
||||
c := b.Cursor()
|
||||
var count int
|
||||
for k, _ := c.Last(); k != nil; k, _ = c.Prev() {
|
||||
@@ -1309,13 +1209,7 @@ func (m *Manager) applyConfigChange(newCfg Config) {
|
||||
}
|
||||
}
|
||||
|
||||
if newCfg.DisablePersist && !oldCfg.DisablePersist {
|
||||
log.Info("Clipboard persist disabled, releasing ownership")
|
||||
m.releaseOwnership()
|
||||
}
|
||||
|
||||
log.Infof("Clipboard config reloaded: disableHistory=%v disablePersist=%v",
|
||||
newCfg.DisableHistory, newCfg.DisablePersist)
|
||||
log.Infof("Clipboard config reloaded: disableHistory=%v", newCfg.DisableHistory)
|
||||
|
||||
m.updateState()
|
||||
m.notifySubscribers()
|
||||
|
||||
@@ -458,7 +458,6 @@ func TestDefaultConfig(t *testing.T) {
|
||||
assert.False(t, cfg.ClearAtStartup)
|
||||
assert.False(t, cfg.Disabled)
|
||||
assert.False(t, cfg.DisableHistory)
|
||||
assert.True(t, cfg.DisablePersist)
|
||||
}
|
||||
|
||||
func TestManager_PostDelegatesToWlContext(t *testing.T) {
|
||||
|
||||
@@ -21,7 +21,6 @@ type Config struct {
|
||||
|
||||
Disabled bool `json:"disabled"`
|
||||
DisableHistory bool `json:"disableHistory"`
|
||||
DisablePersist bool `json:"disablePersist"`
|
||||
}
|
||||
|
||||
func DefaultConfig() Config {
|
||||
@@ -30,7 +29,6 @@ func DefaultConfig() Config {
|
||||
MaxEntrySize: 5 * 1024 * 1024,
|
||||
AutoClearDays: 0,
|
||||
ClearAtStartup: false,
|
||||
DisablePersist: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package cups
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -156,9 +158,42 @@ func (m *Manager) PurgeJobs(printerName string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func resolveIPFromURI(uri string) string {
|
||||
parsed, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
host := parsed.Hostname()
|
||||
if host == "" {
|
||||
return ""
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return ip.String()
|
||||
}
|
||||
addrs, err := net.LookupIP(host)
|
||||
if err != nil || len(addrs) == 0 {
|
||||
return ""
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if v4 := addr.To4(); v4 != nil {
|
||||
return v4.String()
|
||||
}
|
||||
}
|
||||
return addrs[0].String()
|
||||
}
|
||||
|
||||
func (m *Manager) GetDevices() ([]Device, error) {
|
||||
if m.pkHelper != nil {
|
||||
return m.pkHelper.DevicesGet(10, 0, nil, nil)
|
||||
devices, err := m.pkHelper.DevicesGet(10, 0, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range devices {
|
||||
if devices[i].Class == "network" {
|
||||
devices[i].IP = resolveIPFromURI(devices[i].URI)
|
||||
}
|
||||
}
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
deviceAttrs, err := m.client.GetDevices()
|
||||
@@ -176,6 +211,9 @@ func (m *Manager) GetDevices() ([]Device, error) {
|
||||
ID: getStringAttr(attrs, "device-id"),
|
||||
Location: getStringAttr(attrs, "device-location"),
|
||||
}
|
||||
if device.Class == "network" {
|
||||
device.IP = resolveIPFromURI(uri)
|
||||
}
|
||||
devices = append(devices, device)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ type Device struct {
|
||||
MakeModel string `json:"makeModel"`
|
||||
ID string `json:"id"`
|
||||
Location string `json:"location"`
|
||||
IP string `json:"ip,omitempty"`
|
||||
}
|
||||
|
||||
type PPD struct {
|
||||
|
||||
@@ -33,7 +33,7 @@ func (b *NetworkManagerBackend) ListVPNProfiles() ([]VPNProfile, error) {
|
||||
return nil, fmt.Errorf("failed to get connections: %w", err)
|
||||
}
|
||||
|
||||
var profiles []VPNProfile
|
||||
profiles := []VPNProfile{}
|
||||
for _, conn := range connections {
|
||||
settings, err := conn.GetSettings()
|
||||
if err != nil {
|
||||
@@ -101,7 +101,7 @@ func (b *NetworkManagerBackend) ListActiveVPN() ([]VPNActive, error) {
|
||||
return nil, fmt.Errorf("failed to get active connections: %w", err)
|
||||
}
|
||||
|
||||
var active []VPNActive
|
||||
active := []VPNActive{}
|
||||
for _, activeConn := range activeConns {
|
||||
connType, err := activeConn.GetPropertyType()
|
||||
if err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/network"
|
||||
serverPlugins "github.com/AvengeMedia/DankMaterialShell/core/internal/server/plugins"
|
||||
serverThemes "github.com/AvengeMedia/DankMaterialShell/core/internal/server/themes"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
|
||||
)
|
||||
@@ -37,6 +38,11 @@ func RouteRequest(conn net.Conn, req models.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(req.Method, "themes.") {
|
||||
serverThemes.HandleRequest(conn, req)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(req.Method, "loginctl.") {
|
||||
if loginctlManager == nil {
|
||||
models.RespondError(conn, req.ID, "loginctl manager not initialized")
|
||||
@@ -204,9 +210,6 @@ func handleClipboardSetConfig(conn net.Conn, req models.Request) {
|
||||
if v, ok := req.Params["disableHistory"].(bool); ok {
|
||||
cfg.DisableHistory = v
|
||||
}
|
||||
if v, ok := req.Params["disablePersist"].(bool); ok {
|
||||
cfg.DisablePersist = v
|
||||
}
|
||||
|
||||
if err := clipboard.SaveConfig(cfg); err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
|
||||
27
core/internal/server/themes/handlers.go
Normal file
27
core/internal/server/themes/handlers.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
)
|
||||
|
||||
func HandleRequest(conn net.Conn, req models.Request) {
|
||||
switch req.Method {
|
||||
case "themes.list":
|
||||
HandleList(conn, req)
|
||||
case "themes.listInstalled":
|
||||
HandleListInstalled(conn, req)
|
||||
case "themes.install":
|
||||
HandleInstall(conn, req)
|
||||
case "themes.uninstall":
|
||||
HandleUninstall(conn, req)
|
||||
case "themes.update":
|
||||
HandleUpdate(conn, req)
|
||||
case "themes.search":
|
||||
HandleSearch(conn, req)
|
||||
default:
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
|
||||
}
|
||||
}
|
||||
52
core/internal/server/themes/install.go
Normal file
52
core/internal/server/themes/install.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
|
||||
)
|
||||
|
||||
func HandleInstall(conn net.Conn, req models.Request) {
|
||||
idOrName, ok := req.Params["name"].(string)
|
||||
if !ok {
|
||||
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
|
||||
return
|
||||
}
|
||||
|
||||
registry, err := themes.NewRegistry()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
themeList, err := registry.List()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to list themes: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
theme := themes.FindByIDOrName(idOrName, themeList)
|
||||
if theme == nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("theme not found: %s", idOrName))
|
||||
return
|
||||
}
|
||||
|
||||
manager, err := themes.NewManager()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
registryThemeDir := registry.GetThemeDir(theme.SourceDir)
|
||||
if err := manager.Install(*theme, registryThemeDir); err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to install theme: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
models.Respond(conn, req.ID, models.SuccessResult{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("theme installed: %s", theme.Name),
|
||||
})
|
||||
}
|
||||
54
core/internal/server/themes/list.go
Normal file
54
core/internal/server/themes/list.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
|
||||
)
|
||||
|
||||
func HandleList(conn net.Conn, req models.Request) {
|
||||
registry, err := themes.NewRegistry()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
themeList, err := registry.List()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to list themes: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
manager, err := themes.NewManager()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
result := make([]ThemeInfo, len(themeList))
|
||||
for i, t := range themeList {
|
||||
installed, _ := manager.IsInstalled(t)
|
||||
info := ThemeInfo{
|
||||
ID: t.ID,
|
||||
Name: t.Name,
|
||||
Version: t.Version,
|
||||
Author: t.Author,
|
||||
Description: t.Description,
|
||||
PreviewPath: t.PreviewPath,
|
||||
SourceDir: t.SourceDir,
|
||||
Installed: installed,
|
||||
FirstParty: isFirstParty(t.Author),
|
||||
}
|
||||
addVariantsInfo(&info, t.Variants)
|
||||
result[i] = info
|
||||
}
|
||||
|
||||
models.Respond(conn, req.ID, result)
|
||||
}
|
||||
|
||||
func isFirstParty(author string) bool {
|
||||
return strings.EqualFold(author, "Avenge Media") || strings.EqualFold(author, "AvengeMedia")
|
||||
}
|
||||
157
core/internal/server/themes/list_installed.go
Normal file
157
core/internal/server/themes/list_installed.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
|
||||
)
|
||||
|
||||
func addVariantsInfo(info *ThemeInfo, variants *themes.ThemeVariants) {
|
||||
if variants == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if variants.Type == "multi" {
|
||||
if len(variants.Flavors) == 0 && len(variants.Accents) == 0 {
|
||||
return
|
||||
}
|
||||
info.HasVariants = true
|
||||
info.Variants = &VariantsInfo{
|
||||
Type: "multi",
|
||||
Flavors: make([]FlavorInfo, len(variants.Flavors)),
|
||||
Accents: make([]AccentInfo, len(variants.Accents)),
|
||||
}
|
||||
if variants.Defaults != nil {
|
||||
info.Variants.Defaults = &MultiDefaults{
|
||||
Dark: variants.Defaults.Dark,
|
||||
Light: variants.Defaults.Light,
|
||||
}
|
||||
}
|
||||
for i, f := range variants.Flavors {
|
||||
mode := ""
|
||||
switch {
|
||||
case f.Dark.Primary != "" && f.Light.Primary != "":
|
||||
mode = "both"
|
||||
case f.Dark.Primary != "":
|
||||
mode = "dark"
|
||||
case f.Light.Primary != "":
|
||||
mode = "light"
|
||||
default:
|
||||
if f.Dark.Surface != "" {
|
||||
mode = "dark"
|
||||
} else if f.Light.Surface != "" {
|
||||
mode = "light"
|
||||
}
|
||||
}
|
||||
info.Variants.Flavors[i] = FlavorInfo{ID: f.ID, Name: f.Name, Mode: mode}
|
||||
}
|
||||
for i, a := range variants.Accents {
|
||||
color := ""
|
||||
if colors, ok := a.FlavorColors["mocha"]; ok && colors.Primary != "" {
|
||||
color = colors.Primary
|
||||
} else if colors, ok := a.FlavorColors["latte"]; ok && colors.Primary != "" {
|
||||
color = colors.Primary
|
||||
} else {
|
||||
for _, c := range a.FlavorColors {
|
||||
if c.Primary != "" {
|
||||
color = c.Primary
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
info.Variants.Accents[i] = AccentInfo{ID: a.ID, Name: a.Name, Color: color}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if len(variants.Options) == 0 {
|
||||
return
|
||||
}
|
||||
info.HasVariants = true
|
||||
info.Variants = &VariantsInfo{
|
||||
Default: variants.Default,
|
||||
Options: make([]VariantInfo, len(variants.Options)),
|
||||
}
|
||||
for i, v := range variants.Options {
|
||||
info.Variants.Options[i] = VariantInfo{ID: v.ID, Name: v.Name}
|
||||
}
|
||||
}
|
||||
|
||||
func HandleListInstalled(conn net.Conn, req models.Request) {
|
||||
manager, err := themes.NewManager()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
installedIDs, err := manager.ListInstalled()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to list installed themes: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
registry, err := themes.NewRegistry()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
allThemes, err := registry.List()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to list themes: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
themeMap := make(map[string]themes.Theme)
|
||||
for _, t := range allThemes {
|
||||
themeMap[t.ID] = t
|
||||
}
|
||||
|
||||
result := make([]ThemeInfo, 0, len(installedIDs))
|
||||
for _, id := range installedIDs {
|
||||
if theme, ok := themeMap[id]; ok {
|
||||
hasUpdate := false
|
||||
if hasUpdates, err := manager.HasUpdates(id, theme); err == nil {
|
||||
hasUpdate = hasUpdates
|
||||
}
|
||||
|
||||
info := ThemeInfo{
|
||||
ID: theme.ID,
|
||||
Name: theme.Name,
|
||||
Version: theme.Version,
|
||||
Author: theme.Author,
|
||||
Description: theme.Description,
|
||||
SourceDir: id,
|
||||
FirstParty: isFirstParty(theme.Author),
|
||||
HasUpdate: hasUpdate,
|
||||
}
|
||||
addVariantsInfo(&info, theme.Variants)
|
||||
result = append(result, info)
|
||||
} else {
|
||||
installed, err := manager.GetInstalledTheme(id)
|
||||
if err != nil {
|
||||
result = append(result, ThemeInfo{
|
||||
ID: id,
|
||||
Name: id,
|
||||
SourceDir: id,
|
||||
})
|
||||
continue
|
||||
}
|
||||
info := ThemeInfo{
|
||||
ID: installed.ID,
|
||||
Name: installed.Name,
|
||||
Version: installed.Version,
|
||||
Author: installed.Author,
|
||||
Description: installed.Description,
|
||||
SourceDir: id,
|
||||
FirstParty: isFirstParty(installed.Author),
|
||||
}
|
||||
addVariantsInfo(&info, installed.Variants)
|
||||
result = append(result, info)
|
||||
}
|
||||
}
|
||||
|
||||
models.Respond(conn, req.ID, result)
|
||||
}
|
||||
53
core/internal/server/themes/search.go
Normal file
53
core/internal/server/themes/search.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
|
||||
)
|
||||
|
||||
func HandleSearch(conn net.Conn, req models.Request) {
|
||||
query, ok := req.Params["query"].(string)
|
||||
if !ok {
|
||||
models.RespondError(conn, req.ID, "missing or invalid 'query' parameter")
|
||||
return
|
||||
}
|
||||
|
||||
registry, err := themes.NewRegistry()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
themeList, err := registry.List()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to list themes: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
searchResults := themes.FuzzySearch(query, themeList)
|
||||
|
||||
manager, err := themes.NewManager()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
result := make([]ThemeInfo, len(searchResults))
|
||||
for i, t := range searchResults {
|
||||
installed, _ := manager.IsInstalled(t)
|
||||
result[i] = ThemeInfo{
|
||||
ID: t.ID,
|
||||
Name: t.Name,
|
||||
Version: t.Version,
|
||||
Author: t.Author,
|
||||
Description: t.Description,
|
||||
Installed: installed,
|
||||
FirstParty: isFirstParty(t.Author),
|
||||
}
|
||||
}
|
||||
|
||||
models.Respond(conn, req.ID, result)
|
||||
}
|
||||
47
core/internal/server/themes/types.go
Normal file
47
core/internal/server/themes/types.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package themes
|
||||
|
||||
type VariantInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type FlavorInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
}
|
||||
|
||||
type AccentInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color,omitempty"`
|
||||
}
|
||||
|
||||
type MultiDefaults struct {
|
||||
Dark map[string]string `json:"dark,omitempty"`
|
||||
Light map[string]string `json:"light,omitempty"`
|
||||
}
|
||||
|
||||
type VariantsInfo struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Default string `json:"default,omitempty"`
|
||||
Defaults *MultiDefaults `json:"defaults,omitempty"`
|
||||
Options []VariantInfo `json:"options,omitempty"`
|
||||
Flavors []FlavorInfo `json:"flavors,omitempty"`
|
||||
Accents []AccentInfo `json:"accents,omitempty"`
|
||||
}
|
||||
|
||||
type ThemeInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
PreviewPath string `json:"previewPath,omitempty"`
|
||||
SourceDir string `json:"sourceDir,omitempty"`
|
||||
Installed bool `json:"installed,omitempty"`
|
||||
FirstParty bool `json:"firstParty,omitempty"`
|
||||
HasUpdate bool `json:"hasUpdate,omitempty"`
|
||||
HasVariants bool `json:"hasVariants,omitempty"`
|
||||
Variants *VariantsInfo `json:"variants,omitempty"`
|
||||
}
|
||||
63
core/internal/server/themes/uninstall.go
Normal file
63
core/internal/server/themes/uninstall.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
|
||||
)
|
||||
|
||||
func HandleUninstall(conn net.Conn, req models.Request) {
|
||||
idOrName, ok := req.Params["name"].(string)
|
||||
if !ok {
|
||||
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
|
||||
return
|
||||
}
|
||||
|
||||
manager, err := themes.NewManager()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
registry, err := themes.NewRegistry()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
themeList, _ := registry.List()
|
||||
theme := themes.FindByIDOrName(idOrName, themeList)
|
||||
|
||||
if theme != nil {
|
||||
installed, err := manager.IsInstalled(*theme)
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to check if theme is installed: %v", err))
|
||||
return
|
||||
}
|
||||
if !installed {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("theme not installed: %s", idOrName))
|
||||
return
|
||||
}
|
||||
if err := manager.Uninstall(*theme); err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to uninstall theme: %v", err))
|
||||
return
|
||||
}
|
||||
models.Respond(conn, req.ID, models.SuccessResult{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("theme uninstalled: %s", theme.Name),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := manager.UninstallByID(idOrName); err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("theme not found: %s", idOrName))
|
||||
return
|
||||
}
|
||||
|
||||
models.Respond(conn, req.ID, models.SuccessResult{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("theme uninstalled: %s", idOrName),
|
||||
})
|
||||
}
|
||||
57
core/internal/server/themes/update.go
Normal file
57
core/internal/server/themes/update.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/themes"
|
||||
)
|
||||
|
||||
func HandleUpdate(conn net.Conn, req models.Request) {
|
||||
idOrName, ok := req.Params["name"].(string)
|
||||
if !ok {
|
||||
models.RespondError(conn, req.ID, "missing or invalid 'name' parameter")
|
||||
return
|
||||
}
|
||||
|
||||
manager, err := themes.NewManager()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
registry, err := themes.NewRegistry()
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
themeList, _ := registry.List()
|
||||
theme := themes.FindByIDOrName(idOrName, themeList)
|
||||
|
||||
if theme == nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("theme not found in registry: %s", idOrName))
|
||||
return
|
||||
}
|
||||
|
||||
installed, err := manager.IsInstalled(*theme)
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to check if theme is installed: %v", err))
|
||||
return
|
||||
}
|
||||
if !installed {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("theme not installed: %s", idOrName))
|
||||
return
|
||||
}
|
||||
|
||||
if err := manager.Update(*theme); err != nil {
|
||||
models.RespondError(conn, req.ID, fmt.Sprintf("failed to update theme: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
models.Respond(conn, req.ID, models.SuccessResult{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("theme updated: %s", theme.Name),
|
||||
})
|
||||
}
|
||||
@@ -145,6 +145,7 @@ func (m *Manager) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEven
|
||||
handle.SetNameHandler(func(e wlr_output_management.ZwlrOutputHeadV1NameEvent) {
|
||||
log.Debugf("WlrOutput: Head %d name: %s", headID, e.Name)
|
||||
head.name = e.Name
|
||||
head.ready = true
|
||||
m.post(func() {
|
||||
m.updateState()
|
||||
})
|
||||
@@ -240,6 +241,7 @@ func (m *Manager) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEven
|
||||
handle.SetAdaptiveSyncHandler(func(e wlr_output_management.ZwlrOutputHeadV1AdaptiveSyncEvent) {
|
||||
log.Debugf("WlrOutput: Head %d adaptive sync: %d", headID, e.State)
|
||||
head.adaptiveSync = e.State
|
||||
head.adaptiveSyncSupported = true
|
||||
m.post(func() {
|
||||
m.updateState()
|
||||
})
|
||||
@@ -251,11 +253,11 @@ func (m *Manager) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEven
|
||||
|
||||
m.heads.Delete(headID)
|
||||
|
||||
m.post(func() {
|
||||
m.wlMutex.Lock()
|
||||
handle.Release()
|
||||
m.wlMutex.Unlock()
|
||||
m.wlMutex.Lock()
|
||||
handle.Release()
|
||||
m.wlMutex.Unlock()
|
||||
|
||||
m.post(func() {
|
||||
m.updateState()
|
||||
})
|
||||
})
|
||||
@@ -310,11 +312,11 @@ func (m *Manager) handleMode(headID uint32, e wlr_output_management.ZwlrOutputHe
|
||||
|
||||
m.modes.Delete(modeID)
|
||||
|
||||
m.post(func() {
|
||||
m.wlMutex.Lock()
|
||||
handle.Release()
|
||||
m.wlMutex.Unlock()
|
||||
m.wlMutex.Lock()
|
||||
handle.Release()
|
||||
m.wlMutex.Unlock()
|
||||
|
||||
m.post(func() {
|
||||
m.updateState()
|
||||
})
|
||||
})
|
||||
@@ -328,6 +330,10 @@ func (m *Manager) updateState() {
|
||||
return true
|
||||
}
|
||||
|
||||
if !head.ready {
|
||||
return true
|
||||
}
|
||||
|
||||
modes := make([]OutputMode, 0)
|
||||
var currentMode *OutputMode
|
||||
|
||||
@@ -355,22 +361,23 @@ func (m *Manager) updateState() {
|
||||
}
|
||||
|
||||
output := Output{
|
||||
Name: head.name,
|
||||
Description: head.description,
|
||||
Make: head.make,
|
||||
Model: head.model,
|
||||
SerialNumber: head.serialNumber,
|
||||
PhysicalWidth: head.physicalWidth,
|
||||
PhysicalHeight: head.physicalHeight,
|
||||
Enabled: head.enabled,
|
||||
X: head.x,
|
||||
Y: head.y,
|
||||
Transform: head.transform,
|
||||
Scale: head.scale,
|
||||
CurrentMode: currentMode,
|
||||
Modes: modes,
|
||||
AdaptiveSync: head.adaptiveSync,
|
||||
ID: head.id,
|
||||
Name: head.name,
|
||||
Description: head.description,
|
||||
Make: head.make,
|
||||
Model: head.model,
|
||||
SerialNumber: head.serialNumber,
|
||||
PhysicalWidth: head.physicalWidth,
|
||||
PhysicalHeight: head.physicalHeight,
|
||||
Enabled: head.enabled,
|
||||
X: head.x,
|
||||
Y: head.y,
|
||||
Transform: head.transform,
|
||||
Scale: head.scale,
|
||||
CurrentMode: currentMode,
|
||||
Modes: modes,
|
||||
AdaptiveSync: head.adaptiveSync,
|
||||
AdaptiveSyncSupported: head.adaptiveSyncSupported,
|
||||
ID: head.id,
|
||||
}
|
||||
outputs = append(outputs, output)
|
||||
return true
|
||||
|
||||
@@ -17,22 +17,23 @@ type OutputMode struct {
|
||||
}
|
||||
|
||||
type Output struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Make string `json:"make"`
|
||||
Model string `json:"model"`
|
||||
SerialNumber string `json:"serialNumber"`
|
||||
PhysicalWidth int32 `json:"physicalWidth"`
|
||||
PhysicalHeight int32 `json:"physicalHeight"`
|
||||
Enabled bool `json:"enabled"`
|
||||
X int32 `json:"x"`
|
||||
Y int32 `json:"y"`
|
||||
Transform int32 `json:"transform"`
|
||||
Scale float64 `json:"scale"`
|
||||
CurrentMode *OutputMode `json:"currentMode"`
|
||||
Modes []OutputMode `json:"modes"`
|
||||
AdaptiveSync uint32 `json:"adaptiveSync"`
|
||||
ID uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Make string `json:"make"`
|
||||
Model string `json:"model"`
|
||||
SerialNumber string `json:"serialNumber"`
|
||||
PhysicalWidth int32 `json:"physicalWidth"`
|
||||
PhysicalHeight int32 `json:"physicalHeight"`
|
||||
Enabled bool `json:"enabled"`
|
||||
X int32 `json:"x"`
|
||||
Y int32 `json:"y"`
|
||||
Transform int32 `json:"transform"`
|
||||
Scale float64 `json:"scale"`
|
||||
CurrentMode *OutputMode `json:"currentMode"`
|
||||
Modes []OutputMode `json:"modes"`
|
||||
AdaptiveSync uint32 `json:"adaptiveSync"`
|
||||
AdaptiveSyncSupported bool `json:"adaptiveSyncSupported"`
|
||||
ID uint32 `json:"id"`
|
||||
}
|
||||
|
||||
type State struct {
|
||||
@@ -72,24 +73,26 @@ type Manager struct {
|
||||
}
|
||||
|
||||
type headState struct {
|
||||
id uint32
|
||||
handle *wlr_output_management.ZwlrOutputHeadV1
|
||||
name string
|
||||
description string
|
||||
make string
|
||||
model string
|
||||
serialNumber string
|
||||
physicalWidth int32
|
||||
physicalHeight int32
|
||||
enabled bool
|
||||
x int32
|
||||
y int32
|
||||
transform int32
|
||||
scale float64
|
||||
currentModeID uint32
|
||||
modeIDs []uint32
|
||||
adaptiveSync uint32
|
||||
finished bool
|
||||
id uint32
|
||||
handle *wlr_output_management.ZwlrOutputHeadV1
|
||||
name string
|
||||
description string
|
||||
make string
|
||||
model string
|
||||
serialNumber string
|
||||
physicalWidth int32
|
||||
physicalHeight int32
|
||||
enabled bool
|
||||
x int32
|
||||
y int32
|
||||
transform int32
|
||||
scale float64
|
||||
currentModeID uint32
|
||||
modeIDs []uint32
|
||||
adaptiveSync uint32
|
||||
adaptiveSyncSupported bool
|
||||
finished bool
|
||||
ready bool
|
||||
}
|
||||
|
||||
type modeState struct {
|
||||
@@ -168,7 +171,7 @@ func stateChanged(old, new *State) bool {
|
||||
if oldOut.Transform != newOut.Transform || oldOut.Scale != newOut.Scale {
|
||||
return true
|
||||
}
|
||||
if oldOut.AdaptiveSync != newOut.AdaptiveSync {
|
||||
if oldOut.AdaptiveSync != newOut.AdaptiveSync || oldOut.AdaptiveSyncSupported != newOut.AdaptiveSyncSupported {
|
||||
return true
|
||||
}
|
||||
if (oldOut.CurrentMode == nil) != (newOut.CurrentMode == nil) {
|
||||
|
||||
258
core/internal/themes/manager.go
Normal file
258
core/internal/themes/manager.go
Normal file
@@ -0,0 +1,258 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
fs afero.Fs
|
||||
themesDir string
|
||||
}
|
||||
|
||||
func NewManager() (*Manager, error) {
|
||||
return NewManagerWithFs(afero.NewOsFs())
|
||||
}
|
||||
|
||||
func NewManagerWithFs(fs afero.Fs) (*Manager, error) {
|
||||
themesDir := getThemesDir()
|
||||
return &Manager{
|
||||
fs: fs,
|
||||
themesDir: themesDir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getThemesDir() string {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
log.Error("failed to get user config dir", "err", err)
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(configDir, "DankMaterialShell", "themes")
|
||||
}
|
||||
|
||||
func (m *Manager) IsInstalled(theme Theme) (bool, error) {
|
||||
path := m.getInstalledPath(theme.ID)
|
||||
exists, err := afero.Exists(m.fs, path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (m *Manager) getInstalledDir(themeID string) string {
|
||||
return filepath.Join(m.themesDir, themeID)
|
||||
}
|
||||
|
||||
func (m *Manager) getInstalledPath(themeID string) string {
|
||||
return filepath.Join(m.getInstalledDir(themeID), "theme.json")
|
||||
}
|
||||
|
||||
func (m *Manager) Install(theme Theme, registryThemeDir string) error {
|
||||
themeDir := m.getInstalledDir(theme.ID)
|
||||
|
||||
exists, err := afero.DirExists(m.fs, themeDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if theme exists: %w", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return fmt.Errorf("theme already installed: %s", theme.Name)
|
||||
}
|
||||
|
||||
if err := m.fs.MkdirAll(themeDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create theme directory: %w", err)
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(theme, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal theme: %w", err)
|
||||
}
|
||||
|
||||
themePath := filepath.Join(themeDir, "theme.json")
|
||||
if err := afero.WriteFile(m.fs, themePath, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write theme file: %w", err)
|
||||
}
|
||||
|
||||
m.copyPreviewFiles(registryThemeDir, themeDir, theme)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) copyPreviewFiles(srcDir, dstDir string, theme Theme) {
|
||||
previews := []string{"preview-dark.svg", "preview-light.svg"}
|
||||
|
||||
if theme.Variants != nil {
|
||||
for _, v := range theme.Variants.Options {
|
||||
previews = append(previews,
|
||||
fmt.Sprintf("preview-%s.svg", v.ID),
|
||||
fmt.Sprintf("preview-%s-dark.svg", v.ID),
|
||||
fmt.Sprintf("preview-%s-light.svg", v.ID),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for _, preview := range previews {
|
||||
srcPath := filepath.Join(srcDir, preview)
|
||||
if exists, _ := afero.Exists(m.fs, srcPath); !exists {
|
||||
continue
|
||||
}
|
||||
data, err := afero.ReadFile(m.fs, srcPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
dstPath := filepath.Join(dstDir, preview)
|
||||
_ = afero.WriteFile(m.fs, dstPath, data, 0644)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) InstallFromRegistry(registry *Registry, themeID string) error {
|
||||
theme, err := registry.Get(themeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
registryThemeDir := registry.GetThemeDir(theme.SourceDir)
|
||||
return m.Install(*theme, registryThemeDir)
|
||||
}
|
||||
|
||||
func (m *Manager) Update(theme Theme) error {
|
||||
themePath := m.getInstalledPath(theme.ID)
|
||||
|
||||
exists, err := afero.Exists(m.fs, themePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if theme exists: %w", err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return fmt.Errorf("theme not installed: %s", theme.Name)
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(theme, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal theme: %w", err)
|
||||
}
|
||||
|
||||
if err := afero.WriteFile(m.fs, themePath, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write theme file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Uninstall(theme Theme) error {
|
||||
return m.UninstallByID(theme.ID)
|
||||
}
|
||||
|
||||
func (m *Manager) UninstallByID(themeID string) error {
|
||||
themeDir := m.getInstalledDir(themeID)
|
||||
|
||||
exists, err := afero.DirExists(m.fs, themeDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if theme exists: %w", err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return fmt.Errorf("theme not installed: %s", themeID)
|
||||
}
|
||||
|
||||
if err := m.fs.RemoveAll(themeDir); err != nil {
|
||||
return fmt.Errorf("failed to remove theme: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) ListInstalled() ([]string, error) {
|
||||
exists, err := afero.DirExists(m.fs, m.themesDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
entries, err := afero.ReadDir(m.fs, m.themesDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read themes directory: %w", err)
|
||||
}
|
||||
|
||||
var installed []string
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
themeID := entry.Name()
|
||||
themePath := filepath.Join(m.themesDir, themeID, "theme.json")
|
||||
if exists, _ := afero.Exists(m.fs, themePath); exists {
|
||||
installed = append(installed, themeID)
|
||||
}
|
||||
}
|
||||
|
||||
return installed, nil
|
||||
}
|
||||
|
||||
func (m *Manager) GetInstalledTheme(themeID string) (*Theme, error) {
|
||||
themePath := m.getInstalledPath(themeID)
|
||||
|
||||
data, err := afero.ReadFile(m.fs, themePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read theme file: %w", err)
|
||||
}
|
||||
|
||||
var theme Theme
|
||||
if err := json.Unmarshal(data, &theme); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse theme file: %w", err)
|
||||
}
|
||||
|
||||
return &theme, nil
|
||||
}
|
||||
|
||||
func (m *Manager) HasUpdates(themeID string, registryTheme Theme) (bool, error) {
|
||||
installed, err := m.GetInstalledTheme(themeID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return compareVersions(installed.Version, registryTheme.Version) < 0, nil
|
||||
}
|
||||
|
||||
func compareVersions(installed, registry string) int {
|
||||
installedParts := strings.Split(installed, ".")
|
||||
registryParts := strings.Split(registry, ".")
|
||||
|
||||
maxLen := len(installedParts)
|
||||
if len(registryParts) > maxLen {
|
||||
maxLen = len(registryParts)
|
||||
}
|
||||
|
||||
for i := 0; i < maxLen; i++ {
|
||||
var installedNum, registryNum int
|
||||
if i < len(installedParts) {
|
||||
fmt.Sscanf(installedParts[i], "%d", &installedNum)
|
||||
}
|
||||
if i < len(registryParts) {
|
||||
fmt.Sscanf(registryParts[i], "%d", ®istryNum)
|
||||
}
|
||||
|
||||
if installedNum < registryNum {
|
||||
return -1
|
||||
}
|
||||
if installedNum > registryNum {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Manager) GetThemesDir() string {
|
||||
return m.themesDir
|
||||
}
|
||||
309
core/internal/themes/registry.go
Normal file
309
core/internal/themes/registry.go
Normal file
@@ -0,0 +1,309 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-git/go-git/v6"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
const registryRepo = "https://github.com/AvengeMedia/dms-plugin-registry.git"
|
||||
|
||||
type ColorScheme struct {
|
||||
Primary string `json:"primary,omitempty"`
|
||||
PrimaryText string `json:"primaryText,omitempty"`
|
||||
PrimaryContainer string `json:"primaryContainer,omitempty"`
|
||||
Secondary string `json:"secondary,omitempty"`
|
||||
Surface string `json:"surface,omitempty"`
|
||||
SurfaceText string `json:"surfaceText,omitempty"`
|
||||
SurfaceVariant string `json:"surfaceVariant,omitempty"`
|
||||
SurfaceVariantText string `json:"surfaceVariantText,omitempty"`
|
||||
SurfaceTint string `json:"surfaceTint,omitempty"`
|
||||
Background string `json:"background,omitempty"`
|
||||
BackgroundText string `json:"backgroundText,omitempty"`
|
||||
Outline string `json:"outline,omitempty"`
|
||||
SurfaceContainer string `json:"surfaceContainer,omitempty"`
|
||||
SurfaceContainerHigh string `json:"surfaceContainerHigh,omitempty"`
|
||||
SurfaceContainerHighest string `json:"surfaceContainerHighest,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Warning string `json:"warning,omitempty"`
|
||||
Info string `json:"info,omitempty"`
|
||||
}
|
||||
|
||||
type ThemeVariant struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Dark ColorScheme `json:"dark,omitempty"`
|
||||
Light ColorScheme `json:"light,omitempty"`
|
||||
}
|
||||
|
||||
type ThemeFlavor struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Dark ColorScheme `json:"dark,omitempty"`
|
||||
Light ColorScheme `json:"light,omitempty"`
|
||||
}
|
||||
|
||||
type ThemeAccent struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
FlavorColors map[string]ColorScheme `json:"-"`
|
||||
}
|
||||
|
||||
func (a *ThemeAccent) UnmarshalJSON(data []byte) error {
|
||||
var raw map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
a.FlavorColors = make(map[string]ColorScheme)
|
||||
var mErr error
|
||||
for key, value := range raw {
|
||||
switch key {
|
||||
case "id":
|
||||
mErr = errors.Join(mErr, json.Unmarshal(value, &a.ID))
|
||||
case "name":
|
||||
mErr = errors.Join(mErr, json.Unmarshal(value, &a.Name))
|
||||
default:
|
||||
var colors ColorScheme
|
||||
if err := json.Unmarshal(value, &colors); err == nil {
|
||||
a.FlavorColors[key] = colors
|
||||
} else {
|
||||
mErr = errors.Join(mErr, fmt.Errorf("failed to unmarshal flavor colors for key %s: %w", key, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
return mErr
|
||||
}
|
||||
|
||||
func (a ThemeAccent) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]any{
|
||||
"id": a.ID,
|
||||
"name": a.Name,
|
||||
}
|
||||
for k, v := range a.FlavorColors {
|
||||
m[k] = v
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
type MultiVariantDefaults struct {
|
||||
Dark map[string]string `json:"dark,omitempty"`
|
||||
Light map[string]string `json:"light,omitempty"`
|
||||
}
|
||||
|
||||
type ThemeVariants struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Default string `json:"default,omitempty"`
|
||||
Defaults *MultiVariantDefaults `json:"defaults,omitempty"`
|
||||
Options []ThemeVariant `json:"options,omitempty"`
|
||||
Flavors []ThemeFlavor `json:"flavors,omitempty"`
|
||||
Accents []ThemeAccent `json:"accents,omitempty"`
|
||||
}
|
||||
|
||||
type Theme struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Author string `json:"author"`
|
||||
Description string `json:"description"`
|
||||
Dark ColorScheme `json:"dark"`
|
||||
Light ColorScheme `json:"light"`
|
||||
Variants *ThemeVariants `json:"variants,omitempty"`
|
||||
PreviewPath string `json:"-"`
|
||||
SourceDir string `json:"sourceDir,omitempty"`
|
||||
}
|
||||
|
||||
type GitClient interface {
|
||||
PlainClone(path string, url string) error
|
||||
Pull(path string) error
|
||||
}
|
||||
|
||||
type realGitClient struct{}
|
||||
|
||||
func (g *realGitClient) PlainClone(path string, url string) error {
|
||||
_, err := git.PlainClone(path, &git.CloneOptions{
|
||||
URL: url,
|
||||
Progress: os.Stdout,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *realGitClient) Pull(path string) error {
|
||||
repo, err := git.PlainOpen(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
worktree, err := repo.Worktree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = worktree.Pull(&git.PullOptions{})
|
||||
if err != nil && err.Error() != "already up-to-date" {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Registry struct {
|
||||
fs afero.Fs
|
||||
cacheDir string
|
||||
themes []Theme
|
||||
git GitClient
|
||||
}
|
||||
|
||||
func NewRegistry() (*Registry, error) {
|
||||
return NewRegistryWithFs(afero.NewOsFs())
|
||||
}
|
||||
|
||||
func NewRegistryWithFs(fs afero.Fs) (*Registry, error) {
|
||||
cacheDir := getCacheDir()
|
||||
return &Registry{
|
||||
fs: fs,
|
||||
cacheDir: cacheDir,
|
||||
git: &realGitClient{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getCacheDir() string {
|
||||
return filepath.Join(os.TempDir(), "dankdots-plugin-registry")
|
||||
}
|
||||
|
||||
func (r *Registry) Update() error {
|
||||
exists, err := afero.DirExists(r.fs, r.cacheDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check cache directory: %w", err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
if err := r.fs.MkdirAll(filepath.Dir(r.cacheDir), 0755); err != nil {
|
||||
return fmt.Errorf("failed to create cache directory: %w", err)
|
||||
}
|
||||
|
||||
if err := r.git.PlainClone(r.cacheDir, registryRepo); err != nil {
|
||||
return fmt.Errorf("failed to clone registry: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err := r.git.Pull(r.cacheDir); err != nil {
|
||||
if err := r.fs.RemoveAll(r.cacheDir); err != nil {
|
||||
return fmt.Errorf("failed to remove corrupted registry: %w", err)
|
||||
}
|
||||
|
||||
if err := r.fs.MkdirAll(filepath.Dir(r.cacheDir), 0755); err != nil {
|
||||
return fmt.Errorf("failed to create cache directory: %w", err)
|
||||
}
|
||||
|
||||
if err := r.git.PlainClone(r.cacheDir, registryRepo); err != nil {
|
||||
return fmt.Errorf("failed to re-clone registry: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r.loadThemes()
|
||||
}
|
||||
|
||||
func (r *Registry) loadThemes() error {
|
||||
themesDir := filepath.Join(r.cacheDir, "themes")
|
||||
|
||||
entries, err := afero.ReadDir(r.fs, themesDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read themes directory: %w", err)
|
||||
}
|
||||
|
||||
r.themes = []Theme{}
|
||||
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
themeDir := filepath.Join(themesDir, entry.Name())
|
||||
themeFile := filepath.Join(themeDir, "theme.json")
|
||||
|
||||
data, err := afero.ReadFile(r.fs, themeFile)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var theme Theme
|
||||
if err := json.Unmarshal(data, &theme); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if theme.ID == "" {
|
||||
theme.ID = entry.Name()
|
||||
}
|
||||
theme.SourceDir = entry.Name()
|
||||
|
||||
previewPath := filepath.Join(themeDir, "preview.svg")
|
||||
if exists, _ := afero.Exists(r.fs, previewPath); exists {
|
||||
theme.PreviewPath = previewPath
|
||||
}
|
||||
|
||||
r.themes = append(r.themes, theme)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) List() ([]Theme, error) {
|
||||
if len(r.themes) == 0 {
|
||||
if err := r.Update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return SortByFirstParty(r.themes), nil
|
||||
}
|
||||
|
||||
func (r *Registry) Search(query string) ([]Theme, error) {
|
||||
allThemes, err := r.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if query == "" {
|
||||
return allThemes, nil
|
||||
}
|
||||
|
||||
return SortByFirstParty(FuzzySearch(query, allThemes)), nil
|
||||
}
|
||||
|
||||
func (r *Registry) Get(idOrName string) (*Theme, error) {
|
||||
themes, err := r.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, t := range themes {
|
||||
if t.ID == idOrName {
|
||||
return &t, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range themes {
|
||||
if t.Name == idOrName {
|
||||
return &t, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("theme not found: %s", idOrName)
|
||||
}
|
||||
|
||||
func (r *Registry) GetThemeSourcePath(themeID string) string {
|
||||
return filepath.Join(r.cacheDir, "themes", themeID, "theme.json")
|
||||
}
|
||||
|
||||
func (r *Registry) GetThemeDir(themeID string) string {
|
||||
return filepath.Join(r.cacheDir, "themes", themeID)
|
||||
}
|
||||
|
||||
func SortByFirstParty(themes []Theme) []Theme {
|
||||
return themes
|
||||
}
|
||||
40
core/internal/themes/search.go
Normal file
40
core/internal/themes/search.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package themes
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
|
||||
)
|
||||
|
||||
func FuzzySearch(query string, themes []Theme) []Theme {
|
||||
if query == "" {
|
||||
return themes
|
||||
}
|
||||
|
||||
queryLower := strings.ToLower(query)
|
||||
return utils.Filter(themes, func(t Theme) bool {
|
||||
return fuzzyMatch(queryLower, strings.ToLower(t.Name)) ||
|
||||
fuzzyMatch(queryLower, strings.ToLower(t.Description)) ||
|
||||
fuzzyMatch(queryLower, strings.ToLower(t.Author))
|
||||
})
|
||||
}
|
||||
|
||||
func fuzzyMatch(query, text string) bool {
|
||||
queryIdx := 0
|
||||
for _, char := range text {
|
||||
if queryIdx < len(query) && char == rune(query[queryIdx]) {
|
||||
queryIdx++
|
||||
}
|
||||
}
|
||||
return queryIdx == len(query)
|
||||
}
|
||||
|
||||
func FindByIDOrName(idOrName string, themes []Theme) *Theme {
|
||||
if t, found := utils.Find(themes, func(t Theme) bool { return t.ID == idOrName }); found {
|
||||
return &t
|
||||
}
|
||||
if t, found := utils.Find(themes, func(t Theme) bool { return t.Name == idOrName }); found {
|
||||
return &t
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
dms-git (1.0.2+git2528.d336866f) nightly; urgency=medium
|
||||
dms-git (1.0.2+git2528.d336866fdb1) nightly; urgency=medium
|
||||
|
||||
* Git snapshot (commit 2528: d336866f)
|
||||
|
||||
@@ -16,23 +16,6 @@ dms-git (1.0.2+git2518.a783d650) nightly; urgency=medium
|
||||
|
||||
-- Avenge Media <AvengeMedia.US@gmail.com> Sat, 13 Dec 2025 15:11:40 +0000
|
||||
|
||||
dms-git (1.0.2+git2510.0f89886c) nightly; urgency=medium
|
||||
|
||||
* Git snapshot (commit 2510: 0f89886c)
|
||||
|
||||
-- Avenge Media <AvengeMedia.US@gmail.com> Sat, 13 Dec 2025 06:46:43 +0000
|
||||
|
||||
dms-git (1.0.2+git2507.b2ac9c6c) nightly; urgency=medium
|
||||
|
||||
* Git snapshot (commit 2507: b2ac9c6c)
|
||||
|
||||
-- Avenge Media <AvengeMedia.US@gmail.com> Sat, 13 Dec 2025 06:18:05 +0000
|
||||
|
||||
dms-git (1.0.2+git2505.82f881af) nightly; urgency=medium
|
||||
|
||||
* Git snapshot (commit 2505: 82f881af)
|
||||
|
||||
-- Avenge Media <AvengeMedia.US@gmail.com> Sat, 13 Dec 2025 05:55:03 +0000
|
||||
|
||||
dms-git (1.0.0+git2419.993f14a3) nightly; urgency=medium
|
||||
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
<service name="download_url">
|
||||
<param name="protocol">https</param>
|
||||
<param name="host">github.com</param>
|
||||
<param name="path">/AvengeMedia/DankMaterialShell/archive/refs/tags/v1.0.2.tar.gz</param>
|
||||
<param name="path">/AvengeMedia/DankMaterialShell/archive/refs/tags/v1.0.3.tar.gz</param>
|
||||
<param name="filename">dms-source.tar.gz</param>
|
||||
</service>
|
||||
<!-- Download amd64 binary -->
|
||||
<service name="download_url">
|
||||
<param name="protocol">https</param>
|
||||
<param name="host">github.com</param>
|
||||
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.0.2/dms-distropkg-amd64.gz</param>
|
||||
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.0.3/dms-distropkg-amd64.gz</param>
|
||||
</service>
|
||||
<!-- Download arm64 binary -->
|
||||
<service name="download_url">
|
||||
<param name="protocol">https</param>
|
||||
<param name="host">github.com</param>
|
||||
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.0.2/dms-distropkg-arm64.gz</param>
|
||||
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.0.3/dms-distropkg-arm64.gz</param>
|
||||
</service>
|
||||
</services>
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
dms (1.0.2ppa6) unstable; urgency=medium
|
||||
dms (1.0.3db1) unstable; urgency=medium
|
||||
|
||||
* Rebuild to fix repository metadata issues
|
||||
* Update to v1.0.3 stable release
|
||||
|
||||
-- Avenge Media <AvengeMedia.US@gmail.com> Mon, 16 Dec 2025 10:00:00 +0000
|
||||
|
||||
dms (1.0.2db1) unstable; urgency=medium
|
||||
|
||||
* Update to v1.0.2 stable release
|
||||
|
||||
-- Avenge Media <AvengeMedia.US@gmail.com> Sat, 13 Dec 2025 06:47:39 +0000
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||
Package: dms
|
||||
Architecture: amd64
|
||||
Depends: ${misc:Depends},
|
||||
quickshell-git | quickshell,
|
||||
quickshell | quickshell-git,
|
||||
accountsservice,
|
||||
cava,
|
||||
cliphist,
|
||||
|
||||
135
distro/fedora/dms-git.spec
Normal file
135
distro/fedora/dms-git.spec
Normal file
@@ -0,0 +1,135 @@
|
||||
# Spec for DMS - uses rpkg macros for git builds
|
||||
|
||||
%global debug_package %{nil}
|
||||
%global version {{{ git_repo_version }}}
|
||||
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||
|
||||
Name: dms
|
||||
Epoch: 2
|
||||
Version: %{version}
|
||||
Release: 1%{?dist}
|
||||
Summary: %{pkg_summary}
|
||||
|
||||
License: MIT
|
||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
VCS: {{{ git_repo_vcs }}}
|
||||
Source0: {{{ git_repo_pack }}}
|
||||
|
||||
BuildRequires: git-core
|
||||
BuildRequires: gzip
|
||||
BuildRequires: golang >= 1.24
|
||||
BuildRequires: make
|
||||
BuildRequires: wget
|
||||
BuildRequires: systemd-rpm-macros
|
||||
|
||||
# Core requirements
|
||||
Requires: (quickshell-git or quickshell)
|
||||
Requires: accountsservice
|
||||
Requires: dms-cli = %{epoch}:%{version}-%{release}
|
||||
Requires: dgop
|
||||
|
||||
# Core utilities (Highly recommended for DMS functionality)
|
||||
Recommends: cava
|
||||
Recommends: danksearch
|
||||
Recommends: matugen
|
||||
Recommends: quickshell-git
|
||||
Recommends: wl-clipboard
|
||||
|
||||
# Recommended system packages
|
||||
Recommends: NetworkManager
|
||||
Recommends: qt6-qtmultimedia
|
||||
Suggests: qt6ct
|
||||
|
||||
%description
|
||||
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
||||
and optimized for the niri, hyprland, sway, and dwl (MangoWC) compositors. Features notifications,
|
||||
app launcher, wallpaper customization, and fully customizable with plugins.
|
||||
|
||||
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
|
||||
process monitoring, notification center, clipboard history, dock, control center,
|
||||
lock screen, and comprehensive plugin system.
|
||||
|
||||
%package -n dms-cli
|
||||
Summary: DankMaterialShell CLI tool
|
||||
License: MIT
|
||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
|
||||
%description -n dms-cli
|
||||
Command-line interface for DankMaterialShell configuration and management.
|
||||
Provides native DBus bindings, NetworkManager integration, and system utilities.
|
||||
|
||||
%prep
|
||||
{{{ git_repo_setup_macro }}}
|
||||
|
||||
%build
|
||||
# Build DMS CLI from source (core/subdirectory)
|
||||
VERSION="%{version}"
|
||||
COMMIT=$(echo "%{version}" | grep -oP '[a-f0-9]{7,}' | head -n1 || echo "unknown")
|
||||
|
||||
cd core
|
||||
make dist VERSION="$VERSION" COMMIT="$COMMIT"
|
||||
|
||||
%install
|
||||
# Install dms-cli binary (built from source)
|
||||
case "%{_arch}" in
|
||||
x86_64)
|
||||
DMS_BINARY="dms-linux-amd64"
|
||||
;;
|
||||
aarch64)
|
||||
DMS_BINARY="dms-linux-arm64"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported architecture: %{_arch}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
install -Dm755 core/bin/${DMS_BINARY} %{buildroot}%{_bindir}/dms
|
||||
|
||||
# Shell completions
|
||||
install -d %{buildroot}%{_datadir}/bash-completion/completions
|
||||
install -d %{buildroot}%{_datadir}/zsh/site-functions
|
||||
install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
||||
core/bin/${DMS_BINARY} completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||
core/bin/${DMS_BINARY} completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||
core/bin/${DMS_BINARY} completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||
|
||||
# Install systemd user service
|
||||
install -Dm644 assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||
|
||||
install -Dm644 assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop
|
||||
install -Dm644 assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||
|
||||
# Install shell files to shared data location
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||
cp -r quickshell/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||
|
||||
# Remove build files
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
|
||||
|
||||
echo "%{version}" > %{buildroot}%{_datadir}/quickshell/dms/VERSION
|
||||
|
||||
%posttrans
|
||||
# Signal running DMS instances to reload
|
||||
pkill -USR1 -x dms >/dev/null 2>&1 || :
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc CONTRIBUTING.md
|
||||
%doc quickshell/README.md
|
||||
%{_datadir}/quickshell/dms/
|
||||
%{_userunitdir}/dms.service
|
||||
%{_datadir}/applications/dms-open.desktop
|
||||
%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||
|
||||
%files -n dms-cli
|
||||
%{_bindir}/dms
|
||||
%{_datadir}/bash-completion/completions/dms
|
||||
%{_datadir}/zsh/site-functions/_dms
|
||||
%{_datadir}/fish/vendor_completions.d/dms.fish
|
||||
|
||||
%changelog
|
||||
{{{ git_repo_changelog }}}
|
||||
@@ -1,22 +1,22 @@
|
||||
# Spec for DMS Greeter - Git builds using rpkg macros
|
||||
# Spec for DMS Greeter - Stable releases
|
||||
|
||||
%global debug_package %{nil}
|
||||
%global version {{{ git_repo_version }}}
|
||||
%global version VERSION_PLACEHOLDER
|
||||
%global pkg_summary DankMaterialShell greeter for greetd
|
||||
|
||||
Name: dms-greeter
|
||||
Version: %{version}
|
||||
Release: 0.git%{?dist}
|
||||
Release: RELEASE_PLACEHOLDER%{?dist}
|
||||
Summary: %{pkg_summary}
|
||||
|
||||
License: MIT
|
||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
VCS: {{{ git_repo_vcs }}}
|
||||
Source0: {{{ git_repo_pack }}}
|
||||
|
||||
BuildRequires: git-core
|
||||
# For the _tmpfilesdir macro.
|
||||
BuildRequires: systemd-rpm-macros
|
||||
Source0: dms-qml.tar.gz
|
||||
|
||||
BuildRequires: gzip
|
||||
BuildRequires: wget
|
||||
BuildRequires: systemd-rpm-macros
|
||||
|
||||
Requires: greetd
|
||||
Requires: (quickshell-git or quickshell)
|
||||
@@ -24,14 +24,11 @@ Requires(post): /usr/sbin/useradd
|
||||
Requires(post): /usr/sbin/groupadd
|
||||
|
||||
Recommends: policycoreutils-python-utils
|
||||
Recommends: setfacl
|
||||
Recommends: acl
|
||||
Suggests: niri
|
||||
Suggests: hyprland
|
||||
Suggests: sway
|
||||
|
||||
# Provides: greetd-dms-greeter = %{version}-%{release}
|
||||
# Conflicts: greetd-dms-greeter
|
||||
|
||||
%description
|
||||
DankMaterialShell greeter for greetd login manager. A modern, Material Design 3
|
||||
inspired greeter interface built with Quickshell for Wayland compositors.
|
||||
@@ -41,31 +38,26 @@ compositor detection and configuration. Features session selection, user
|
||||
authentication, and dynamic theming.
|
||||
|
||||
%prep
|
||||
{{{ git_repo_setup_macro }}}
|
||||
%setup -q -c -n dms-qml
|
||||
|
||||
%build
|
||||
|
||||
%install
|
||||
# Install greeter files to shared data location (from quickshell/ subdirectory)
|
||||
# Install greeter files to shared data location
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms-greeter
|
||||
cp -r quickshell/* %{buildroot}%{_datadir}/quickshell/dms-greeter/
|
||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms-greeter/
|
||||
|
||||
# Install launcher script
|
||||
install -Dm755 quickshell/Modules/Greetd/assets/dms-greeter %{buildroot}%{_bindir}/dms-greeter
|
||||
install -Dm755 %{_builddir}/dms-qml/Modules/Greetd/assets/dms-greeter %{buildroot}%{_bindir}/dms-greeter
|
||||
|
||||
# Install documentation
|
||||
install -Dm644 quickshell/Modules/Greetd/README.md %{buildroot}%{_docdir}/dms-greeter/README.md
|
||||
install -Dm644 %{_builddir}/dms-qml/Modules/Greetd/README.md %{buildroot}%{_docdir}/dms-greeter/README.md
|
||||
|
||||
# Create cache directory for greeter data
|
||||
install -Dpm0644 quickshell/systemd/tmpfiles-dms-greeter.conf %{buildroot}%{_tmpfilesdir}/dms-greeter.conf
|
||||
install -Dpm0644 %{_builddir}/dms-qml/systemd/tmpfiles-dms-greeter.conf %{buildroot}%{_tmpfilesdir}/dms-greeter.conf
|
||||
|
||||
# Install LICENSE file
|
||||
install -Dm644 LICENSE %{buildroot}%{_docdir}/dms-greeter/LICENSE
|
||||
install -Dm644 %{_builddir}/dms-qml/LICENSE %{buildroot}%{_docdir}/dms-greeter/LICENSE
|
||||
|
||||
# Create greeter home directory
|
||||
install -dm755 %{buildroot}%{_sharedstatedir}/greeter
|
||||
|
||||
# Note: We do NOT install a PAM config here to avoid conflicting with greetd package
|
||||
# Instead, we verify/fix it in %post if needed
|
||||
|
||||
# Note: We do NOT install a PAM config here to avoid conflicting w/greetd packages
|
||||
# Remove build and development files
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/.gitignore
|
||||
@@ -73,9 +65,8 @@ rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/.github
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/distro
|
||||
|
||||
%posttrans
|
||||
# Clean up old installation path from previous versions (only if empty)
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms-greeter" ]; then
|
||||
# Remove directories only if empty (preserves any user-added files)
|
||||
# Remove directories & preserves any user-added files
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms-greeter" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
@@ -89,7 +80,7 @@ fi
|
||||
%{_tmpfilesdir}/%{name}.conf
|
||||
|
||||
%pre
|
||||
# Create greeter user/group if they don't exist (greetd expects this)
|
||||
# Create greeter user/group if they don't exist
|
||||
getent group greeter >/dev/null || groupadd -r greeter
|
||||
getent passwd greeter >/dev/null || \
|
||||
useradd -r -g greeter -d %{_sharedstatedir}/greeter -s /bin/bash \
|
||||
@@ -127,7 +118,6 @@ chown -R greeter:greeter %{_sharedstatedir}/greeter 2>/dev/null || true
|
||||
# Verify PAM configuration - only fix if insufficient
|
||||
PAM_CONFIG="/etc/pam.d/greetd"
|
||||
if [ ! -f "$PAM_CONFIG" ]; then
|
||||
# PAM config doesn't exist - create it
|
||||
cat > "$PAM_CONFIG" << 'PAM_EOF'
|
||||
#%PAM-1.0
|
||||
auth substack system-auth
|
||||
@@ -149,7 +139,6 @@ PAM_EOF
|
||||
# Only show message on initial install
|
||||
[ "$1" -eq 1 ] && echo "Created PAM configuration for greetd"
|
||||
elif ! grep -q "pam_systemd\|system-auth" "$PAM_CONFIG"; then
|
||||
# PAM config exists but looks insufficient - back it up and replace
|
||||
cp "$PAM_CONFIG" "$PAM_CONFIG.backup-dms-greeter"
|
||||
cat > "$PAM_CONFIG" << 'PAM_EOF'
|
||||
#%PAM-1.0
|
||||
@@ -198,9 +187,8 @@ command = "/usr/bin/dms-greeter --command COMPOSITOR_PLACEHOLDER"
|
||||
GREETD_EOF
|
||||
sed -i "s|COMPOSITOR_PLACEHOLDER|$COMPOSITOR|" "$GREETD_CONFIG"
|
||||
CONFIG_STATUS="Created new config with $COMPOSITOR ✓"
|
||||
# If config exists and doesn't have dms-greeter, update it
|
||||
|
||||
elif ! grep -q "dms-greeter" "$GREETD_CONFIG"; then
|
||||
# Backup existing config
|
||||
BACKUP_FILE="${GREETD_CONFIG}.backup-$(date +%%Y%%m%%d-%%H%%M%%S)"
|
||||
cp "$GREETD_CONFIG" "$BACKUP_FILE" 2>/dev/null || true
|
||||
|
||||
@@ -267,4 +255,6 @@ if [ "$1" -eq 0 ] && [ -x /usr/sbin/semanage ]; then
|
||||
fi
|
||||
|
||||
%changelog
|
||||
{{{ git_repo_changelog }}}
|
||||
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-RELEASE_PLACEHOLDER
|
||||
- Stable release VERSION_PLACEHOLDER
|
||||
- Built from GitHub release
|
||||
|
||||
@@ -1,49 +1,40 @@
|
||||
# Spec for DMS - uses rpkg macros for git builds
|
||||
# Feodra spec for DMS stable releases
|
||||
|
||||
%global debug_package %{nil}
|
||||
%global version {{{ git_repo_version }}}
|
||||
%global version VERSION_PLACEHOLDER
|
||||
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||
|
||||
Name: dms
|
||||
Epoch: 2
|
||||
Version: %{version}
|
||||
Release: 1%{?dist}
|
||||
Release: RELEASE_PLACEHOLDER%{?dist}
|
||||
Summary: %{pkg_summary}
|
||||
|
||||
License: MIT
|
||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
VCS: {{{ git_repo_vcs }}}
|
||||
Source0: {{{ git_repo_pack }}}
|
||||
|
||||
BuildRequires: git-core
|
||||
Source0: dms-qml.tar.gz
|
||||
|
||||
BuildRequires: gzip
|
||||
BuildRequires: golang >= 1.24
|
||||
BuildRequires: make
|
||||
BuildRequires: wget
|
||||
BuildRequires: systemd-rpm-macros
|
||||
|
||||
# Core requirements
|
||||
Requires: (quickshell-git or quickshell)
|
||||
Requires: (quickshell or quickshell-git)
|
||||
Requires: accountsservice
|
||||
Requires: dms-cli = %{epoch}:%{version}-%{release}
|
||||
Requires: dms-cli = %{version}-%{release}
|
||||
Requires: dgop
|
||||
|
||||
# Core utilities (Highly recommended for DMS functionality)
|
||||
Recommends: cava
|
||||
Recommends: cliphist
|
||||
Recommends: danksearch
|
||||
Recommends: matugen
|
||||
Recommends: quickshell-git
|
||||
Recommends: wl-clipboard
|
||||
|
||||
# Recommended system packages
|
||||
Recommends: NetworkManager
|
||||
Recommends: qt6-qtmultimedia
|
||||
Suggests: qt6ct
|
||||
|
||||
%description
|
||||
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
||||
and optimized for the niri, hyprland, sway, and dwl (MangoWC) compositors. Features notifications,
|
||||
and optimized for the niri and hyprland compositors. Features notifications,
|
||||
app launcher, wallpaper customization, and fully customizable with plugins.
|
||||
|
||||
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
|
||||
@@ -60,24 +51,14 @@ Command-line interface for DankMaterialShell configuration and management.
|
||||
Provides native DBus bindings, NetworkManager integration, and system utilities.
|
||||
|
||||
%prep
|
||||
{{{ git_repo_setup_macro }}}
|
||||
%setup -q -c -n dms-qml
|
||||
|
||||
%build
|
||||
# Build DMS CLI from source (core/subdirectory)
|
||||
VERSION="%{version}"
|
||||
COMMIT=$(echo "%{version}" | grep -oP '[a-f0-9]{7,}' | head -n1 || echo "unknown")
|
||||
|
||||
cd core
|
||||
make dist VERSION="$VERSION" COMMIT="$COMMIT"
|
||||
|
||||
%install
|
||||
# Install dms-cli binary (built from source)
|
||||
case "%{_arch}" in
|
||||
x86_64)
|
||||
DMS_BINARY="dms-linux-amd64"
|
||||
ARCH_SUFFIX="amd64"
|
||||
;;
|
||||
aarch64)
|
||||
DMS_BINARY="dms-linux-arm64"
|
||||
ARCH_SUFFIX="arm64"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported architecture: %{_arch}"
|
||||
@@ -85,27 +66,35 @@ case "%{_arch}" in
|
||||
;;
|
||||
esac
|
||||
|
||||
install -Dm755 core/bin/${DMS_BINARY} %{buildroot}%{_bindir}/dms
|
||||
# Download dms-cli for target architecture
|
||||
wget -O %{_builddir}/dms-cli.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-${ARCH_SUFFIX}.gz" || {
|
||||
echo "Failed to download dms-cli for architecture %{_arch}"
|
||||
exit 1
|
||||
}
|
||||
gunzip -c %{_builddir}/dms-cli.gz > %{_builddir}/dms-cli
|
||||
chmod +x %{_builddir}/dms-cli
|
||||
|
||||
%build
|
||||
|
||||
%install
|
||||
install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms
|
||||
|
||||
# Shell completions
|
||||
install -d %{buildroot}%{_datadir}/bash-completion/completions
|
||||
install -d %{buildroot}%{_datadir}/zsh/site-functions
|
||||
install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
||||
core/bin/${DMS_BINARY} completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||
core/bin/${DMS_BINARY} completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||
core/bin/${DMS_BINARY} completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||
%{_builddir}/dms-cli completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||
%{_builddir}/dms-cli completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||
%{_builddir}/dms-cli completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||
|
||||
# Install systemd user service
|
||||
install -Dm644 assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||
install -Dm644 %{_builddir}/dms-qml/assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||
|
||||
install -Dm644 assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop
|
||||
install -Dm644 assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||
install -Dm644 %{_builddir}/dms-qml/assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop
|
||||
install -Dm644 %{_builddir}/dms-qml/assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||
|
||||
# Install shell files to shared data location
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||
cp -r quickshell/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||
|
||||
# Remove build files
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
@@ -119,8 +108,7 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc CONTRIBUTING.md
|
||||
%doc quickshell/README.md
|
||||
%doc README.md CONTRIBUTING.md
|
||||
%{_datadir}/quickshell/dms/
|
||||
%{_userunitdir}/dms.service
|
||||
%{_datadir}/applications/dms-open.desktop
|
||||
@@ -133,4 +121,6 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
|
||||
%{_datadir}/fish/vendor_completions.d/dms.fish
|
||||
|
||||
%changelog
|
||||
{{{ git_repo_changelog }}}
|
||||
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-RELEASE_PLACEHOLDER
|
||||
- Stable release VERSION_PLACEHOLDER
|
||||
- Built from GitHub release
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.programs.dankMaterialShell;
|
||||
cfg = config.programs.dank-material-shell;
|
||||
in
|
||||
{
|
||||
packages = [
|
||||
dmsPkgs.dms-shell
|
||||
]
|
||||
++ lib.optional cfg.enableSystemMonitoring dmsPkgs.dgop
|
||||
++ lib.optional cfg.enableSystemMonitoring cfg.dgop.package
|
||||
++ lib.optionals cfg.enableVPN [
|
||||
pkgs.glib
|
||||
pkgs.networkmanager
|
||||
|
||||
15
distro/nix/dms-rename.nix
Normal file
15
distro/nix/dms-rename.nix
Normal file
@@ -0,0 +1,15 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"programs"
|
||||
"dankMaterialShell"
|
||||
]
|
||||
[
|
||||
"programs"
|
||||
"dank-material-shell"
|
||||
]
|
||||
)
|
||||
];
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
}:
|
||||
let
|
||||
inherit (lib) types;
|
||||
cfg = config.programs.dankMaterialShell.greeter;
|
||||
cfg = config.programs.dank-material-shell.greeter;
|
||||
|
||||
inherit (config.services.greetd.settings.default_session) user;
|
||||
|
||||
@@ -44,19 +44,20 @@ in
|
||||
{
|
||||
imports =
|
||||
let
|
||||
msg = "The option 'programs.dankMaterialShell.greeter.compositor.extraConfig' is deprecated. Please use 'programs.dankMaterialShell.greeter.compositor.customConfig' instead.";
|
||||
msg = "The option 'programs.dank-material-shell.greeter.compositor.extraConfig' is deprecated. Please use 'programs.dank-material-shell.greeter.compositor.customConfig' instead.";
|
||||
in
|
||||
[
|
||||
(lib.mkRemovedOptionModule [
|
||||
"programs"
|
||||
"dankMaterialShell"
|
||||
"dank-material-shell"
|
||||
"greeter"
|
||||
"compositor"
|
||||
"extraConfig"
|
||||
] msg)
|
||||
./dms-rename.nix
|
||||
];
|
||||
|
||||
options.programs.dankMaterialShell.greeter = {
|
||||
options.programs.dank-material-shell.greeter = {
|
||||
enable = lib.mkEnableOption "DankMaterialShell greeter";
|
||||
compositor.name = lib.mkOption {
|
||||
type = types.enum [
|
||||
@@ -177,7 +178,7 @@ in
|
||||
mv dms-colors.json colors.json || :
|
||||
chown ${user}: * || :
|
||||
'';
|
||||
programs.dankMaterialShell.greeter.configFiles = lib.mkIf (cfg.configHome != null) [
|
||||
programs.dank-material-shell.greeter.configFiles = lib.mkIf (cfg.configHome != null) [
|
||||
"${cfg.configHome}/.config/DankMaterialShell/settings.json"
|
||||
"${cfg.configHome}/.local/state/DankMaterialShell/session.json"
|
||||
"${cfg.configHome}/.cache/DankMaterialShell/dms-colors.json"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
...
|
||||
}@args:
|
||||
let
|
||||
cfg = config.programs.dankMaterialShell;
|
||||
cfg = config.programs.dank-material-shell;
|
||||
jsonFormat = pkgs.formats.json { };
|
||||
common = import ./common.nix {
|
||||
inherit
|
||||
@@ -22,16 +22,16 @@ in
|
||||
(import ./options.nix args)
|
||||
(lib.mkRemovedOptionModule [
|
||||
"programs"
|
||||
"dankMaterialShell"
|
||||
"dank-material-shell"
|
||||
"enableNightMode"
|
||||
] "Night mode is now always available.")
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "programs" "dankMaterialShell" "enableSystemd" ]
|
||||
[ "programs" "dankMaterialShell" "systemd" "enable" ]
|
||||
[ "programs" "dank-material-shell" "enableSystemd" ]
|
||||
[ "programs" "dank-material-shell" "systemd" "enable" ]
|
||||
)
|
||||
];
|
||||
|
||||
options.programs.dankMaterialShell = with lib.types; {
|
||||
options.programs.dank-material-shell = with lib.types; {
|
||||
default = {
|
||||
settings = lib.mkOption {
|
||||
type = jsonFormat.type;
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.programs.dankMaterialShell;
|
||||
cfg = config.programs.dank-material-shell;
|
||||
in
|
||||
{
|
||||
options.programs.dankMaterialShell = {
|
||||
imports = [
|
||||
./dms-rename.nix
|
||||
];
|
||||
|
||||
options.programs.dank-material-shell = {
|
||||
niri = {
|
||||
enableKeybinds = lib.mkEnableOption "DankMaterialShell niri keybinds";
|
||||
enableSpawn = lib.mkEnableOption "DankMaterialShell niri spawn-at-startup";
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
...
|
||||
}@args:
|
||||
let
|
||||
cfg = config.programs.dankMaterialShell;
|
||||
cfg = config.programs.dank-material-shell;
|
||||
common = import ./common.nix {
|
||||
inherit
|
||||
config
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
lib,
|
||||
dmsPkgs,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) types;
|
||||
path = [
|
||||
"programs"
|
||||
"dankMaterialShell"
|
||||
"dank-material-shell"
|
||||
];
|
||||
|
||||
builtInRemovedMsg = "This is now built-in in DMS and doesn't need additional dependencies.";
|
||||
@@ -20,46 +21,58 @@ in
|
||||
(lib.mkRemovedOptionModule (
|
||||
path ++ [ "enableSystemSound" ]
|
||||
) "qtmultimedia is now included on dms-shell package.")
|
||||
./dms-rename.nix
|
||||
];
|
||||
|
||||
options.programs.dankMaterialShell = {
|
||||
options.programs.dank-material-shell = {
|
||||
enable = lib.mkEnableOption "DankMaterialShell";
|
||||
|
||||
systemd = {
|
||||
enable = lib.mkEnableOption "DankMaterialShell systemd startup";
|
||||
restartIfChanged = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Auto-restart dms.service when dankMaterialShell changes";
|
||||
description = "Auto-restart dms.service when dank-material-shell changes";
|
||||
};
|
||||
};
|
||||
|
||||
dgop = {
|
||||
package = lib.mkPackageOption pkgs "dgop";
|
||||
};
|
||||
|
||||
enableSystemMonitoring = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Add needed dependencies to use system monitoring widgets";
|
||||
};
|
||||
|
||||
enableVPN = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Add needed dependencies to use the VPN widget";
|
||||
};
|
||||
|
||||
enableDynamicTheming = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Add needed dependencies to have dynamic theming support";
|
||||
};
|
||||
|
||||
enableAudioWavelength = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Add needed dependencies to have audio wavelength support";
|
||||
};
|
||||
|
||||
enableCalendarEvents = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Add calendar events support via khal";
|
||||
};
|
||||
|
||||
quickshell = {
|
||||
package = lib.mkPackageOption dmsPkgs "quickshell" {
|
||||
extraDescription = "The quickshell package to use (defaults to be built from source, in the commit 26531f due to unreleased features used by DMS).";
|
||||
extraDescription = "The quickshell package to use (defaults to be built from source, due to unreleased features used by DMS).";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
%global debug_package %{nil}
|
||||
|
||||
Name: dms
|
||||
Version: 1.0.2
|
||||
Release: 7%{?dist}
|
||||
Version: 1.0.3
|
||||
Release: 1%{?dist}
|
||||
Summary: DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||
|
||||
License: MIT
|
||||
@@ -17,7 +17,7 @@ BuildRequires: gzip
|
||||
BuildRequires: systemd-rpm-macros
|
||||
|
||||
# Core requirements
|
||||
Requires: (quickshell-git or quickshell)
|
||||
Requires: (quickshell or quickshell-git)
|
||||
Requires: accountsservice
|
||||
Requires: dgop
|
||||
|
||||
@@ -105,6 +105,9 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
|
||||
%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||
|
||||
%changelog
|
||||
* Mon Dec 16 2025 AvengeMedia <maintainer@avengemedia.com> - 1.0.3-1
|
||||
- Update to stable v1.0.3 release
|
||||
|
||||
* Fri Dec 12 2025 AvengeMedia <maintainer@avengemedia.com> - 1.0.2-1
|
||||
- Update to stable v1.0.2 release
|
||||
- Bug fixes and improvements
|
||||
|
||||
@@ -2,226 +2,121 @@
|
||||
set -euo pipefail
|
||||
|
||||
# Build SRPM locally with correct tarball and upload to Copr
|
||||
# Usage: ./create-upload-copr.sh VERSION [RELEASE]
|
||||
# Example: ./create-upload-copr.sh 1.0.0 4
|
||||
# Usage: ./copr-upload.sh [PACKAGE] [VERSION] [RELEASE]
|
||||
# Examples:
|
||||
# ./copr-upload.sh dms 1.0.3 1
|
||||
# ./copr-upload.sh dms-greeter 1.0.3 1
|
||||
|
||||
PACKAGE="${1:-dms}"
|
||||
VERSION="${2:-}"
|
||||
RELEASE="${3:-1}"
|
||||
|
||||
VERSION="${1:-1.0.0}"
|
||||
RELEASE="${2:-1}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
echo "Building DMS v${VERSION}-${RELEASE} SRPM for Copr..."
|
||||
# Determine Copr project based on package
|
||||
if [ "$PACKAGE" = "dms" ]; then
|
||||
COPR_PROJECT="avengemedia/dms"
|
||||
elif [ "$PACKAGE" = "dms-greeter" ]; then
|
||||
COPR_PROJECT="avengemedia/danklinux"
|
||||
else
|
||||
echo "❌ Unknown package: $PACKAGE"
|
||||
echo "Supported packages: dms, dms-greeter"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get version from latest release if not provided
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "📦 Determining latest version..."
|
||||
VERSION=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | jq -r '.tag_name' | sed 's/^v//')
|
||||
if [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then
|
||||
echo "❌ Failed to determine version. Please specify manually."
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Using latest version: $VERSION"
|
||||
fi
|
||||
|
||||
echo "Building ${PACKAGE} v${VERSION}-${RELEASE} SRPM for Copr..."
|
||||
|
||||
# Setup build directories
|
||||
mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||
cd ~/rpmbuild/SOURCES
|
||||
|
||||
# Create the corrected QML tarball locally
|
||||
echo "Creating QML tarball with assets..."
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
# Copy quickshell contents to temp
|
||||
cp -r quickshell/* "$TEMP_DIR/"
|
||||
|
||||
# Copy root LICENSE and CONTRIBUTING.md
|
||||
cp LICENSE CONTRIBUTING.md "$TEMP_DIR/"
|
||||
|
||||
# Copy root assets directory (this is what was missing!)
|
||||
cp -r assets "$TEMP_DIR/"
|
||||
|
||||
# Create tarball
|
||||
cd "$TEMP_DIR"
|
||||
tar --exclude='.git' \
|
||||
--exclude='.github' \
|
||||
--exclude='*.tar.gz' \
|
||||
-czf ~/rpmbuild/SOURCES/dms-qml.tar.gz .
|
||||
|
||||
cd ~/rpmbuild/SOURCES
|
||||
echo "Created dms-qml.tar.gz with md5sum: $(md5sum dms-qml.tar.gz | awk '{print $1}')"
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
# Generate spec file
|
||||
echo "Generating spec file..."
|
||||
CHANGELOG_DATE="$(date '+%a %b %d %Y')"
|
||||
|
||||
cat >~/rpmbuild/SPECS/dms.spec <<'SPECEOF'
|
||||
# Spec for DMS stable releases - Built locally
|
||||
|
||||
%global debug_package %{nil}
|
||||
%global version VERSION_PLACEHOLDER
|
||||
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||
|
||||
Name: dms
|
||||
Version: %{version}
|
||||
Release: RELEASE_PLACEHOLDER%{?dist}
|
||||
Summary: %{pkg_summary}
|
||||
|
||||
License: MIT
|
||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
|
||||
Source0: dms-qml.tar.gz
|
||||
|
||||
BuildRequires: gzip
|
||||
BuildRequires: wget
|
||||
BuildRequires: systemd-rpm-macros
|
||||
|
||||
Requires: (quickshell or quickshell-git)
|
||||
Requires: accountsservice
|
||||
Requires: dms-cli = %{version}-%{release}
|
||||
Requires: dgop
|
||||
|
||||
Recommends: cava
|
||||
Recommends: cliphist
|
||||
Recommends: danksearch
|
||||
Recommends: matugen
|
||||
Recommends: wl-clipboard
|
||||
Recommends: NetworkManager
|
||||
Recommends: qt6-qtmultimedia
|
||||
Suggests: qt6ct
|
||||
|
||||
%description
|
||||
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
||||
and optimized for the niri and hyprland compositors. Features notifications,
|
||||
app launcher, wallpaper customization, and fully customizable with plugins.
|
||||
|
||||
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
|
||||
process monitoring, notification center, clipboard history, dock, control center,
|
||||
lock screen, and comprehensive plugin system.
|
||||
|
||||
%package -n dms-cli
|
||||
Summary: DankMaterialShell CLI tool
|
||||
License: MIT
|
||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||
|
||||
%description -n dms-cli
|
||||
Command-line interface for DankMaterialShell configuration and management.
|
||||
Provides native DBus bindings, NetworkManager integration, and system utilities.
|
||||
|
||||
%prep
|
||||
%setup -q -c -n dms-qml
|
||||
|
||||
# Download architecture-specific binaries during build
|
||||
case "%{_arch}" in
|
||||
x86_64)
|
||||
ARCH_SUFFIX="amd64"
|
||||
;;
|
||||
aarch64)
|
||||
ARCH_SUFFIX="arm64"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported architecture: %{_arch}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
wget -O %{_builddir}/dms-cli.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/download/v%{version}/dms-distropkg-${ARCH_SUFFIX}.gz" || {
|
||||
echo "Failed to download dms-cli for architecture %{_arch}"
|
||||
exit 1
|
||||
}
|
||||
gunzip -c %{_builddir}/dms-cli.gz > %{_builddir}/dms-cli
|
||||
chmod +x %{_builddir}/dms-cli
|
||||
|
||||
%build
|
||||
|
||||
%install
|
||||
install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms
|
||||
|
||||
install -d %{buildroot}%{_datadir}/bash-completion/completions
|
||||
install -d %{buildroot}%{_datadir}/zsh/site-functions
|
||||
install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
||||
%{_builddir}/dms-cli completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||
%{_builddir}/dms-cli completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||
%{_builddir}/dms-cli completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||
|
||||
install -Dm644 assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||
|
||||
install -Dm644 assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop
|
||||
install -Dm644 assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||
|
||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
|
||||
|
||||
echo "%{version}" > %{buildroot}%{_datadir}/quickshell/dms/VERSION
|
||||
|
||||
%posttrans
|
||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||
# Download source tarball from GitHub releases
|
||||
echo "📦 Downloading source tarball for v${VERSION}..."
|
||||
if [ ! -f ~/rpmbuild/SOURCES/dms-qml.tar.gz ]; then
|
||||
wget -O ~/rpmbuild/SOURCES/dms-qml.tar.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/download/v${VERSION}/dms-qml.tar.gz" || {
|
||||
echo "❌ Failed to download dms-qml.tar.gz for v${VERSION}"
|
||||
exit 1
|
||||
}
|
||||
echo "✅ Source tarball downloaded"
|
||||
else
|
||||
echo "✅ Source tarball already exists"
|
||||
fi
|
||||
# Signal running DMS instances to reload
|
||||
pkill -USR1 -x dms >/dev/null 2>&1 || :
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc README.md CONTRIBUTING.md
|
||||
%{_datadir}/quickshell/dms/
|
||||
%{_userunitdir}/dms.service
|
||||
%{_datadir}/applications/dms-open.desktop
|
||||
%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||
# Copy and prepare spec file
|
||||
echo "📝 Preparing spec file..."
|
||||
SPEC_FILE="$REPO_ROOT/distro/fedora/${PACKAGE}.spec"
|
||||
if [ ! -f "$SPEC_FILE" ]; then
|
||||
echo "❌ Spec file not found: $SPEC_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
%files -n dms-cli
|
||||
%{_bindir}/dms
|
||||
%{_datadir}/bash-completion/completions/dms
|
||||
%{_datadir}/zsh/site-functions/_dms
|
||||
%{_datadir}/fish/vendor_completions.d/dms.fish
|
||||
cp "$SPEC_FILE" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
||||
|
||||
%changelog
|
||||
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-1
|
||||
- Stable release VERSION_PLACEHOLDER
|
||||
- Built locally with corrected tarball
|
||||
SPECEOF
|
||||
# Replace placeholders in spec file
|
||||
CHANGELOG_DATE="$(date '+%a %b %d %Y')"
|
||||
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
||||
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
||||
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
||||
|
||||
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/dms.spec
|
||||
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/dms.spec
|
||||
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/dms.spec
|
||||
echo "✅ Spec file prepared for ${PACKAGE} v${VERSION}-${RELEASE}"
|
||||
|
||||
# Build SRPM
|
||||
echo "Building SRPM..."
|
||||
echo "🔨 Building SRPM..."
|
||||
cd ~/rpmbuild/SPECS
|
||||
rpmbuild -bs dms.spec
|
||||
rpmbuild -bs "${PACKAGE}".spec
|
||||
|
||||
SRPM=$(ls ~/rpmbuild/SRPMS/dms-"${VERSION}"-*.src.rpm | tail -n 1)
|
||||
SRPM=$(ls ~/rpmbuild/SRPMS/"${PACKAGE}"-"${VERSION}"-*.src.rpm | tail -n 1)
|
||||
if [ ! -f "$SRPM" ]; then
|
||||
echo "Error: SRPM not found!"
|
||||
echo "❌ Error: SRPM not found!"
|
||||
echo "Expected pattern: ${PACKAGE}-${VERSION}-*.src.rpm"
|
||||
ls -la ~/rpmbuild/SRPMS/ || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "SRPM built successfully: $SRPM"
|
||||
echo "✅ SRPM built successfully: $SRPM"
|
||||
|
||||
# Check if copr-cli is installed
|
||||
if ! command -v copr-cli &>/dev/null; then
|
||||
echo ""
|
||||
echo "copr-cli is not installed. Install it with:"
|
||||
echo "⚠️ copr-cli is not installed. Install it with:"
|
||||
echo " pip install copr-cli"
|
||||
echo ""
|
||||
echo "Then configure it with your Copr API token in ~/.config/copr"
|
||||
echo ""
|
||||
echo "SRPM is ready at: $SRPM"
|
||||
echo "Upload manually with: copr-cli build avengemedia/dms $SRPM"
|
||||
echo "Upload manually with: copr-cli build $COPR_PROJECT $SRPM"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Upload to Copr
|
||||
echo ""
|
||||
echo "Uploading to Copr..."
|
||||
if copr-cli build avengemedia/dms "$SRPM" --nowait; then
|
||||
echo "🚀 Uploading to Copr..."
|
||||
if copr-cli build "$COPR_PROJECT" "$SRPM" --nowait; then
|
||||
echo ""
|
||||
echo "Build submitted successfully! Check status at:"
|
||||
echo "https://copr.fedorainfracloud.org/coprs/avengemedia/dms/builds/"
|
||||
echo "✅ Build submitted successfully!"
|
||||
echo "📊 Check status at:"
|
||||
echo " https://copr.fedorainfracloud.org/coprs/${COPR_PROJECT}/builds/"
|
||||
echo ""
|
||||
echo "📦 SRPM location: $SRPM"
|
||||
else
|
||||
echo ""
|
||||
echo "Copr upload failed. You can manually upload the SRPM:"
|
||||
echo " copr-cli build avengemedia/dms $SRPM"
|
||||
echo "❌ Copr upload failed. You can manually upload the SRPM:"
|
||||
echo " copr-cli build $COPR_PROJECT $SRPM"
|
||||
echo ""
|
||||
echo "Or upload via web interface:"
|
||||
echo " https://copr.fedorainfracloud.org/coprs/avengemedia/dms/builds/"
|
||||
echo " https://copr.fedorainfracloud.org/coprs/${COPR_PROJECT}/builds/"
|
||||
echo ""
|
||||
echo "SRPM location: $SRPM"
|
||||
exit 1
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
# ./distro/scripts/obs-upload.sh dms "Update to v1.0.2"
|
||||
# ./distro/scripts/obs-upload.sh debian dms
|
||||
# ./distro/scripts/obs-upload.sh opensuse dms-git
|
||||
# ./distro/scripts/obs-upload.sh debian dms-git 2 # Rebuild with ppa2 suffix
|
||||
# ./distro/scripts/obs-upload.sh dms-git --rebuild=2 # Rebuild with ppa2 suffix (flag syntax)
|
||||
# ./distro/scripts/obs-upload.sh debian dms-git 2 # Rebuild with db2 suffix
|
||||
# ./distro/scripts/obs-upload.sh dms-git --rebuild=2 # Rebuild with db2 suffix (flag syntax)
|
||||
|
||||
set -e
|
||||
|
||||
@@ -126,8 +126,8 @@ check_obs_version_exists() {
|
||||
OBS_VERSION=$(echo "$OBS_SPEC" | grep "^Version:" | awk '{print $2}' | xargs)
|
||||
# Commit hash check for -git packages
|
||||
if [[ "$CHECK_MODE" == "commit" ]] && [[ "$PACKAGE" == *"-git" ]]; then
|
||||
OBS_COMMIT=$(echo "$OBS_VERSION" | grep -oP '\.([a-f0-9]{8})(ppa[0-9]+)?$' | grep -oP '[a-f0-9]{8}' || echo "")
|
||||
NEW_COMMIT=$(echo "$VERSION" | grep -oP '\.([a-f0-9]{8})(ppa[0-9]+)?$' | grep -oP '[a-f0-9]{8}' || echo "")
|
||||
OBS_COMMIT=$(echo "$OBS_VERSION" | grep -oP '\.([a-f0-9]{8})(db[0-9]+)?$' | grep -oP '[a-f0-9]{8}' || echo "")
|
||||
NEW_COMMIT=$(echo "$VERSION" | grep -oP '\.([a-f0-9]{8})(db[0-9]+)?$' | grep -oP '[a-f0-9]{8}' || echo "")
|
||||
|
||||
if [[ -n "$OBS_COMMIT" && -n "$NEW_COMMIT" && "$OBS_COMMIT" == "$NEW_COMMIT" ]]; then
|
||||
echo "⚠️ Commit $NEW_COMMIT already exists in OBS (current version: $OBS_VERSION)"
|
||||
@@ -279,7 +279,8 @@ if [[ -d "distro/debian/$PACKAGE/debian" ]]; then
|
||||
|
||||
# Apply rebuild suffix if specified (must happen before API check)
|
||||
if [[ -n "$REBUILD_RELEASE" ]] && [[ -n "$CHANGELOG_VERSION" ]]; then
|
||||
CHANGELOG_VERSION="${CHANGELOG_VERSION}ppa${REBUILD_RELEASE}"
|
||||
BASE_VERSION=$(echo "$CHANGELOG_VERSION" | sed 's/db[0-9]*$//')
|
||||
CHANGELOG_VERSION="${BASE_VERSION}db${REBUILD_RELEASE}"
|
||||
echo " - Applied rebuild suffix: $CHANGELOG_VERSION"
|
||||
fi
|
||||
|
||||
@@ -307,12 +308,16 @@ if [[ -d "distro/debian/$PACKAGE/debian" ]]; then
|
||||
else
|
||||
# Rebuild number specified - check if this exact version already exists (exact mode)
|
||||
if check_obs_version_exists "$OBS_PROJECT" "$PACKAGE" "$CHANGELOG_VERSION" "exact"; then
|
||||
echo "==> Error: Version $CHANGELOG_VERSION already exists in OBS"
|
||||
echo " This exact version (including ppa${REBUILD_RELEASE}) is already uploaded."
|
||||
echo " To rebuild with a different release number, try incrementing:"
|
||||
echo "==> Version $CHANGELOG_VERSION already exists in OBS"
|
||||
echo " This exact version (including db${REBUILD_RELEASE}) is already uploaded."
|
||||
echo " Skipping upload - nothing to do."
|
||||
echo ""
|
||||
echo " 💡 To rebuild with a different release number, try incrementing:"
|
||||
NEXT_NUM=$((REBUILD_RELEASE + 1))
|
||||
echo " ./distro/scripts/obs-upload.sh $PACKAGE $NEXT_NUM"
|
||||
exit 1
|
||||
echo " REBUILD_RELEASE=$NEXT_NUM"
|
||||
echo ""
|
||||
echo "✓ Exiting gracefully (no changes needed)"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -511,7 +516,7 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
|
||||
|
||||
if [[ -n "$URL_PROTOCOL" && -n "$URL_HOST" && -n "$URL_PATH" ]]; then
|
||||
SOURCE_URL="${URL_PROTOCOL}://${URL_HOST}${URL_PATH}"
|
||||
echo " Downloading source from: $SOURCE_URL"
|
||||
echo "==> Downloading source from: $SOURCE_URL"
|
||||
|
||||
if wget -q -O "$TEMP_DIR/source-archive" "$SOURCE_URL" 2>/dev/null ||
|
||||
curl -L -f -s -o "$TEMP_DIR/source-archive" "$SOURCE_URL" 2>/dev/null; then
|
||||
@@ -534,9 +539,17 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
|
||||
fi
|
||||
SOURCE_DIR=$(cd "$SOURCE_DIR" && pwd)
|
||||
cd "$REPO_ROOT"
|
||||
if [[ "$(pwd)" != "$REPO_ROOT" ]]; then
|
||||
echo "ERROR: Failed to return to REPO_ROOT. Expected: $REPO_ROOT, Got: $(pwd)"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Error: Failed to download source from $SOURCE_URL"
|
||||
echo "Tried both wget and curl. Please check the URL and network connectivity."
|
||||
echo "ERROR: Failed to download source from $SOURCE_URL"
|
||||
echo "Attempted both wget and curl"
|
||||
echo "Please check:"
|
||||
echo " 1. URL is accessible: $SOURCE_URL"
|
||||
echo " 2. _service file has correct version"
|
||||
echo " 3. GitHub releases are available"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
@@ -553,7 +566,7 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " Found source directory: $SOURCE_DIR"
|
||||
echo "==> Found source directory: $SOURCE_DIR"
|
||||
|
||||
# Vendor Go dependencies for dms-git
|
||||
if [[ "$PACKAGE" == "dms-git" ]] && [[ -d "$SOURCE_DIR/core" ]]; then
|
||||
@@ -712,6 +725,10 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
|
||||
TARBALL_BASE=$(basename "$SOURCE_DIR")
|
||||
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/$COMBINED_TARBALL" "$TARBALL_BASE"
|
||||
cd "$REPO_ROOT"
|
||||
if [[ "$(pwd)" != "$REPO_ROOT" ]]; then
|
||||
echo "ERROR: Failed to return to REPO_ROOT after tarball creation"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$PACKAGE" == "dms" ]]; then
|
||||
TARBALL_DIR=$(tar -tzf "$WORK_DIR/$COMBINED_TARBALL" 2>/dev/null | head -1 | cut -d'/' -f1)
|
||||
@@ -723,6 +740,10 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
|
||||
rm -f "$WORK_DIR/$COMBINED_TARBALL"
|
||||
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/$COMBINED_TARBALL" "$TARBALL_BASE"
|
||||
cd "$REPO_ROOT"
|
||||
if [[ "$(pwd)" != "$REPO_ROOT" ]]; then
|
||||
echo "ERROR: Failed to return to REPO_ROOT after tarball recreation"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -796,23 +817,29 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "$WORK_DIR"
|
||||
echo "==> Ensuring we're in the OSC working directory"
|
||||
cd "$WORK_DIR" || {
|
||||
echo "ERROR: Cannot cd to WORK_DIR: $WORK_DIR"
|
||||
echo "DEBUG: Current directory: $(pwd)"
|
||||
echo "DEBUG: WORK_DIR exists: $(test -d "$WORK_DIR" && echo "yes" || echo "no")"
|
||||
exit 1
|
||||
}
|
||||
echo "DEBUG: Successfully entered WORK_DIR: $(pwd)"
|
||||
|
||||
# Server-side cleanup via API
|
||||
echo "==> Cleaning old tarballs from OBS server (prevents downloading 100+ old versions)"
|
||||
OBS_FILES=$(osc api "/source/$OBS_PROJECT/$PACKAGE" 2>/dev/null || echo "")
|
||||
if [[ -n "$OBS_FILES" ]]; then
|
||||
DELETED_COUNT=0
|
||||
KEEP_PATTERN=""
|
||||
KEEP_CURRENT=""
|
||||
if [[ -n "$CHANGELOG_VERSION" ]]; then
|
||||
BASE_KEEP_VERSION=$(echo "$CHANGELOG_VERSION" | sed 's/ppa[0-9]*$//')
|
||||
KEEP_PATTERN="${PACKAGE}_${BASE_KEEP_VERSION}"
|
||||
echo " Keeping tarballs matching: ${KEEP_PATTERN}*"
|
||||
KEEP_CURRENT="${PACKAGE}_${CHANGELOG_VERSION}.tar.gz"
|
||||
echo " Keeping only current version: ${KEEP_CURRENT}"
|
||||
fi
|
||||
|
||||
for old_file in $(echo "$OBS_FILES" | grep -oP '(?<=name=")[^"]*\.(tar\.gz|tar\.xz|tar\.bz2)(?=")' || true); do
|
||||
if [[ -n "$KEEP_PATTERN" ]] && [[ "$old_file" == ${KEEP_PATTERN}* ]]; then
|
||||
echo " - Keeping current version: $old_file"
|
||||
if [[ "$old_file" == "$KEEP_CURRENT" ]]; then
|
||||
echo " - Keeping: $old_file"
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -835,14 +862,11 @@ else
|
||||
echo " ⚠️ Could not fetch file list from server, skipping cleanup"
|
||||
fi
|
||||
|
||||
# Fallback update with --server-side-source-service-files flag only syncs metadata (spec, dsc, _service)
|
||||
# Update working copy to latest revision (without expanding service files to avoid revision conflicts)
|
||||
echo "==> Updating working copy"
|
||||
if ! osc up --server-side-source-service-files 2>/dev/null; then
|
||||
echo " Note: Using regular update (--server-side-source-service-files not supported)"
|
||||
if ! osc up; then
|
||||
echo "Error: Failed to update working copy"
|
||||
exit 1
|
||||
fi
|
||||
if ! osc up 2>/dev/null; then
|
||||
echo "Error: Failed to update working copy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure we're in WORK_DIR and it exists
|
||||
@@ -882,6 +906,15 @@ elif [[ "$UPLOAD_OPENSUSE" == true ]]; then
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [[ "$(pwd)" != "$WORK_DIR" ]]; then
|
||||
echo "ERROR: Lost directory context. Expected: $WORK_DIR, Got: $(pwd)"
|
||||
cd "$WORK_DIR" || {
|
||||
echo "FATAL: Cannot recover - unable to cd to WORK_DIR"
|
||||
exit 1
|
||||
}
|
||||
echo "WARNING: Recovered directory context"
|
||||
fi
|
||||
|
||||
osc addremove 2>&1 | grep -v "Git SCM package" || true
|
||||
|
||||
SOURCE_TARBALL="${PACKAGE}-source.tar.gz"
|
||||
@@ -908,7 +941,7 @@ if ! osc status 2>/dev/null | grep -qE '^[MAD]|^[?]'; then
|
||||
else
|
||||
echo "==> Committing to OBS"
|
||||
set +e
|
||||
osc commit -m "$MESSAGE" 2>&1 | grep -v "Git SCM package" | grep -v "apiurl\|project\|_ObsPrj\|_manifest\|git-obs"
|
||||
osc commit --skip-local-service-run -m "$MESSAGE" 2>&1 | grep -v "Git SCM package" | grep -v "apiurl\|project\|_ObsPrj\|_manifest\|git-obs"
|
||||
COMMIT_EXIT=${PIPESTATUS[0]}
|
||||
set -e
|
||||
if [[ $COMMIT_EXIT -ne 0 ]]; then
|
||||
|
||||
@@ -191,19 +191,8 @@ fi
|
||||
cd "$WORK_PACKAGE_DIR"
|
||||
get_latest_tag() {
|
||||
local repo="$1"
|
||||
if command -v curl &>/dev/null; then
|
||||
LATEST_TAG=$(curl -s "https://api.github.com/repos/$repo/releases/latest" 2>/dev/null | grep '"tag_name":' | sed 's/.*"tag_name": "\(.*\)".*/\1/' | head -1)
|
||||
if [ -n "$LATEST_TAG" ]; then
|
||||
echo "$LATEST_TAG" | sed 's/^v//'
|
||||
return
|
||||
fi
|
||||
fi
|
||||
TEMP_REPO=$(mktemp -d "$TEMP_BASE/ppa_tag_XXXXXX")
|
||||
if git clone --depth=1 --quiet "https://github.com/$repo.git" "$TEMP_REPO" 2>/dev/null; then
|
||||
LATEST_TAG=$(cd "$TEMP_REPO" && git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "")
|
||||
rm -rf "$TEMP_REPO"
|
||||
echo "$LATEST_TAG"
|
||||
fi
|
||||
# Get the latest tag, sorted by version
|
||||
git ls-remote --tags --refs --sort='-v:refname' "https://github.com/$repo.git" | head -n1 | awk -F/ '{print $NF}' | sed 's/^v//'
|
||||
}
|
||||
|
||||
IS_GIT_PACKAGE=false
|
||||
@@ -334,6 +323,17 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "dms-distropkg-arm64.gz" ]; then
|
||||
info "Downloading dms binary for arm64..."
|
||||
# Try to download arm64 binary, but don't fail if it doesn't exist (yet)
|
||||
if wget -O dms-distropkg-arm64.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/download/v${VERSION}/dms-distropkg-arm64.gz"; then
|
||||
success "arm64 binary downloaded"
|
||||
else
|
||||
warn "Failed to download dms-distropkg-arm64.gz (skipping)"
|
||||
rm -f dms-distropkg-arm64.gz
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "dms-source.tar.gz" ]; then
|
||||
info "Downloading dms source for QML files..."
|
||||
if wget -O dms-source.tar.gz "https://github.com/AvengeMedia/DankMaterialShell/archive/refs/tags/v${VERSION}.tar.gz"; then
|
||||
|
||||
@@ -341,6 +341,10 @@ if [ "$KEEP_BUILDS" = "false" ]; then
|
||||
rm -f "$PACKAGE_DIR/dms-distropkg-amd64.gz"
|
||||
REMOVED=$((REMOVED + 1))
|
||||
fi
|
||||
if [ -f "$PACKAGE_DIR/dms-distropkg-arm64.gz" ]; then
|
||||
rm -f "$PACKAGE_DIR/dms-distropkg-arm64.gz"
|
||||
REMOVED=$((REMOVED + 1))
|
||||
fi
|
||||
if [ -f "$PACKAGE_DIR/dms-source.tar.gz" ]; then
|
||||
rm -f "$PACKAGE_DIR/dms-source.tar.gz"
|
||||
REMOVED=$((REMOVED + 1))
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
danksearch (0.0.7ppa3) questing; urgency=medium
|
||||
|
||||
* Rebuild for packaging fixes (ppa3)
|
||||
|
||||
-- Avenge Media <AvengeMedia.US@gmail.com> Fri, 21 Nov 2025 14:19:58 -0500
|
||||
@@ -1,24 +0,0 @@
|
||||
Source: danksearch
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||
Build-Depends: debhelper-compat (= 13)
|
||||
Standards-Version: 4.6.2
|
||||
Homepage: https://github.com/AvengeMedia/danksearch
|
||||
Vcs-Browser: https://github.com/AvengeMedia/danksearch
|
||||
Vcs-Git: https://github.com/AvengeMedia/danksearch.git
|
||||
|
||||
Package: danksearch
|
||||
Architecture: amd64 arm64
|
||||
Depends: ${misc:Depends}
|
||||
Description: Fast file search utility for DMS
|
||||
DankSearch is a fast file search utility designed for DankMaterialShell.
|
||||
It provides efficient file and content search capabilities with minimal
|
||||
dependencies. This package contains the pre-built binary from the official
|
||||
GitHub release.
|
||||
.
|
||||
Features include:
|
||||
- Fast file searching
|
||||
- Lightweight and efficient
|
||||
- Designed for DMS integration
|
||||
- Minimal resource usage
|
||||
@@ -1,24 +0,0 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: danksearch
|
||||
Upstream-Contact: Avenge Media LLC <AvengeMedia.US@gmail.com>
|
||||
Source: https://github.com/AvengeMedia/danksearch
|
||||
|
||||
Files: *
|
||||
Copyright: 2025 Avenge Media LLC
|
||||
License: GPL-3.0-only
|
||||
|
||||
License: GPL-3.0-only
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 3 as
|
||||
published by the Free Software Foundation.
|
||||
.
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
export DH_VERBOSE = 1
|
||||
|
||||
# Detect architecture for selecting correct binary
|
||||
DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
|
||||
|
||||
# Map Debian arch to binary filename
|
||||
ifeq ($(DEB_HOST_ARCH),amd64)
|
||||
BINARY_FILE := dsearch-amd64
|
||||
else ifeq ($(DEB_HOST_ARCH),arm64)
|
||||
BINARY_FILE := dsearch-arm64
|
||||
else
|
||||
$(error Unsupported architecture: $(DEB_HOST_ARCH))
|
||||
endif
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
# Binary is already included in source package (native format)
|
||||
# Downloaded by build-source.sh before upload
|
||||
# Just verify it exists and is executable
|
||||
test -f $(BINARY_FILE) || (echo "ERROR: $(BINARY_FILE) not found!" && exit 1)
|
||||
chmod +x $(BINARY_FILE)
|
||||
|
||||
override_dh_auto_install:
|
||||
# Install binary as danksearch
|
||||
install -Dm755 $(BINARY_FILE) debian/danksearch/usr/bin/danksearch
|
||||
|
||||
override_dh_auto_clean:
|
||||
# Don't delete binaries - they're part of the source package (native format)
|
||||
dh_auto_clean
|
||||
@@ -1 +0,0 @@
|
||||
3.0 (native)
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,9 +0,0 @@
|
||||
dgop (0.1.11ppa2) questing; urgency=medium
|
||||
|
||||
* Rebuild for Questing (25.10) - Ubuntu 25.10+ only
|
||||
* Stateless CPU/GPU monitoring tool
|
||||
* Support for NVIDIA and AMD GPUs
|
||||
* JSON output for integration
|
||||
* Pre-built binary package for amd64 and arm64
|
||||
|
||||
-- Avenge Media <AvengeMedia.US@gmail.com> Sun, 16 Nov 2025 22:50:00 -0500
|
||||
@@ -1,27 +0,0 @@
|
||||
Source: dgop
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Avenge Media <AvengeMedia.US@gmail.com>
|
||||
Build-Depends: debhelper-compat (= 13),
|
||||
wget,
|
||||
gzip
|
||||
Standards-Version: 4.6.2
|
||||
Homepage: https://github.com/AvengeMedia/dgop
|
||||
Vcs-Browser: https://github.com/AvengeMedia/dgop
|
||||
Vcs-Git: https://github.com/AvengeMedia/dgop.git
|
||||
|
||||
Package: dgop
|
||||
Architecture: amd64 arm64
|
||||
Depends: ${misc:Depends}
|
||||
Description: Stateless CPU/GPU monitor for DankMaterialShell
|
||||
DGOP is a stateless system monitoring tool that provides CPU, GPU,
|
||||
memory, and network statistics. Designed for integration with
|
||||
DankMaterialShell but can be used standalone.
|
||||
.
|
||||
Features:
|
||||
- CPU usage monitoring
|
||||
- GPU usage and temperature (NVIDIA, AMD)
|
||||
- Memory and swap statistics
|
||||
- Network traffic monitoring
|
||||
- Zero-state design (no background processes)
|
||||
- JSON output for easy integration
|
||||
@@ -1,27 +0,0 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: dgop
|
||||
Upstream-Contact: Avenge Media LLC <AvengeMedia.US@gmail.com>
|
||||
Source: https://github.com/AvengeMedia/dgop
|
||||
|
||||
Files: *
|
||||
Copyright: 2025 Avenge Media LLC
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
export DH_VERBOSE = 1
|
||||
|
||||
# Extract version from debian/changelog
|
||||
DEB_VERSION := $(shell dpkg-parsechangelog -S Version)
|
||||
# Get upstream version (strip -1ppa1 suffix)
|
||||
UPSTREAM_VERSION := $(shell echo $(DEB_VERSION) | sed 's/-[^-]*$$//')
|
||||
|
||||
# Detect architecture for downloading correct binary
|
||||
DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
|
||||
|
||||
# Map Debian arch to GitHub release arch names
|
||||
ifeq ($(DEB_HOST_ARCH),amd64)
|
||||
GITHUB_ARCH := amd64
|
||||
else ifeq ($(DEB_HOST_ARCH),arm64)
|
||||
GITHUB_ARCH := arm64
|
||||
else
|
||||
$(error Unsupported architecture: $(DEB_HOST_ARCH))
|
||||
endif
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
# Binary is already included in source package (native format)
|
||||
# Just verify it exists and is executable
|
||||
test -f dgop || (echo "ERROR: dgop binary not found!" && exit 1)
|
||||
chmod +x dgop
|
||||
|
||||
override_dh_auto_install:
|
||||
# Install binary
|
||||
install -Dm755 dgop debian/dgop/usr/bin/dgop
|
||||
|
||||
override_dh_auto_clean:
|
||||
# Don't delete dgop binary - it's part of the source package (native format)
|
||||
rm -f dgop.gz
|
||||
dh_auto_clean
|
||||
@@ -1 +0,0 @@
|
||||
3.0 (native)
|
||||
@@ -10,7 +10,7 @@ Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
||||
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||
|
||||
Package: dms-git
|
||||
Architecture: amd64
|
||||
Architecture: any
|
||||
Depends: ${misc:Depends},
|
||||
quickshell-git | quickshell,
|
||||
accountsservice,
|
||||
|
||||
@@ -32,15 +32,17 @@ override_dh_auto_build:
|
||||
sed -i 's/^go 1\.24\.[0-9]*/go 1.24/' dms-git-repo/core/go.mod
|
||||
|
||||
# Build dms-cli from source
|
||||
# Detect architecture
|
||||
$(eval DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH))
|
||||
@if [ -f dms-git-repo/.dms-version ]; then \
|
||||
. dms-git-repo/.dms-version; \
|
||||
echo "Building with VERSION=$$VERSION COMMIT=$$COMMIT"; \
|
||||
cd dms-git-repo/core && $(MAKE) GOFLAGS="-mod=vendor" dist ARCH=amd64 VERSION="$$VERSION" COMMIT="$$COMMIT"; \
|
||||
echo "Building with VERSION=$$VERSION COMMIT=$$COMMIT ARCH=$(DEB_HOST_ARCH)"; \
|
||||
cd dms-git-repo/core && $(MAKE) GOFLAGS="-mod=vendor" dist ARCH=$(DEB_HOST_ARCH) VERSION="$$VERSION" COMMIT="$$COMMIT"; \
|
||||
else \
|
||||
echo "Warning: .dms-version not found, building without version info"; \
|
||||
cd dms-git-repo/core && $(MAKE) GOFLAGS="-mod=vendor" dist ARCH=amd64; \
|
||||
cd dms-git-repo/core && $(MAKE) GOFLAGS="-mod=vendor" dist ARCH=$(DEB_HOST_ARCH); \
|
||||
fi
|
||||
cp dms-git-repo/core/bin/dms-linux-amd64 dms
|
||||
cp dms-git-repo/core/bin/dms-linux-$(DEB_HOST_ARCH) dms
|
||||
chmod +x dms
|
||||
|
||||
override_dh_auto_install:
|
||||
|
||||
@@ -9,15 +9,15 @@ Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
||||
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||
|
||||
Package: dms-greeter
|
||||
Architecture: all
|
||||
Architecture: any
|
||||
Depends: ${misc:Depends},
|
||||
greetd,
|
||||
quickshell-git | quickshell
|
||||
greetd,
|
||||
quickshell-git | quickshell
|
||||
Recommends: niri | hyprland | sway
|
||||
Description: DankMaterialShell greeter for greetd
|
||||
DankMaterialShell greeter for greetd login manager. A modern, Material Design 3
|
||||
inspired greeter interface built with Quickshell for Wayland compositors.
|
||||
.
|
||||
Supports multiple compositors including Niri, Hyprland, and Sway with automatic
|
||||
compositor detection and configuration. Features session selection, user
|
||||
authentication, and dynamic theming.
|
||||
DankMaterialShell greeter for greetd login manager. A modern, Material Design 3
|
||||
inspired greeter interface built with Quickshell for Wayland compositors.
|
||||
.
|
||||
Supports multiple compositors including Niri, Hyprland, and Sway with automatic
|
||||
compositor detection and configuration. Features session selection, user
|
||||
authentication, and dynamic theming.
|
||||
|
||||
@@ -9,9 +9,9 @@ Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
||||
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||
|
||||
Package: dms
|
||||
Architecture: amd64
|
||||
Architecture: any
|
||||
Depends: ${misc:Depends},
|
||||
quickshell-git | quickshell,
|
||||
quickshell | quickshell-git,
|
||||
accountsservice,
|
||||
cava,
|
||||
cliphist,
|
||||
|
||||
@@ -19,11 +19,14 @@ override_dh_installsystemd:
|
||||
override_dh_auto_build:
|
||||
# All files are included in source package (downloaded by build-source.sh)
|
||||
# Launchpad build environment has no internet access
|
||||
test -f dms-distropkg-amd64.gz || (echo "ERROR: dms-distropkg-amd64.gz not found!" && exit 1)
|
||||
$(eval DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH))
|
||||
@echo "Building for architecture: $(DEB_HOST_ARCH)"
|
||||
|
||||
test -f dms-distropkg-$(DEB_HOST_ARCH).gz || (echo "ERROR: dms-distropkg-$(DEB_HOST_ARCH).gz not found!" && exit 1)
|
||||
test -f dms-source.tar.gz || (echo "ERROR: dms-source.tar.gz not found!" && exit 1)
|
||||
|
||||
# Extract pre-built binary
|
||||
gunzip -c dms-distropkg-amd64.gz > dms
|
||||
gunzip -c dms-distropkg-$(DEB_HOST_ARCH).gz > dms
|
||||
chmod +x dms
|
||||
|
||||
# Extract source tarball for QML files
|
||||
|
||||
46
docs/theme_gruvbox_material_hard.json
Normal file
46
docs/theme_gruvbox_material_hard.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"dark": {
|
||||
"name": "Gruvbox Material Dark Hard",
|
||||
"surface": "#202020",
|
||||
"surfaceText": "#fbf1c7",
|
||||
"surfaceVariant": "#2a2827",
|
||||
"surfaceVariantText": "#ebdbb2",
|
||||
"background": "#2a2827",
|
||||
"backgroundText": "#ddc7a1",
|
||||
"outline": "#5a524c",
|
||||
"surfaceContainer": "#2a2827",
|
||||
"surfaceContainerHigh": "#504945",
|
||||
"surfaceContainerHighest": "#5a524c",
|
||||
"primary": "#a9b665",
|
||||
"secondary": "#d8a657",
|
||||
"primaryText": "#292828",
|
||||
"primaryContainer": "#798247",
|
||||
"surfaceTint": "#7daea3",
|
||||
"error": "#ea6962",
|
||||
"warning": "#d3869b",
|
||||
"info": "#89b482",
|
||||
"matugen_type": "scheme-tonal-spot"
|
||||
},
|
||||
"light": {
|
||||
"name": "Gruvbox Material Light Hard",
|
||||
"surface": "#f9f5d7",
|
||||
"surfaceText": "#282828",
|
||||
"surfaceVariant": "#fbf1c7",
|
||||
"surfaceVariantText": "#3c3836",
|
||||
"background": "#fbf1c7",
|
||||
"backgroundText": "#654735",
|
||||
"outline": "#a89984",
|
||||
"surfaceContainer": "#fbf1c7",
|
||||
"surfaceContainerHigh": "#e0cfa9",
|
||||
"surfaceContainerHighest": "#a89984",
|
||||
"primary": "#6c782e",
|
||||
"secondary": "#b47109",
|
||||
"primaryText": "#292828",
|
||||
"primaryContainer": "#566026",
|
||||
"surfaceTint": "#45707a",
|
||||
"error": "#c14a4a",
|
||||
"warning": "#945e80",
|
||||
"info": "#4c7a5d",
|
||||
"matugen_type": "scheme-tonal-spot"
|
||||
}
|
||||
}
|
||||
46
docs/theme_gruvbox_material_medium.json
Normal file
46
docs/theme_gruvbox_material_medium.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"dark": {
|
||||
"name": "Gruvbox Material Dark Medium",
|
||||
"surface": "#292828",
|
||||
"surfaceText": "#fbf1c7",
|
||||
"surfaceVariant": "#32302f",
|
||||
"surfaceVariantText": "#ebdbb2",
|
||||
"background": "#32302f",
|
||||
"backgroundText": "#ddc7a1",
|
||||
"outline": "#665c54",
|
||||
"surfaceContainer": "#32302f",
|
||||
"surfaceContainerHigh": "#504945",
|
||||
"surfaceContainerHighest": "#665c54",
|
||||
"primary": "#a9b665",
|
||||
"secondary": "#d8a657",
|
||||
"primaryText": "#292828",
|
||||
"primaryContainer": "#798247",
|
||||
"surfaceTint": "#7daea3",
|
||||
"error": "#ea6962",
|
||||
"warning": "#d3869b",
|
||||
"info": "#89b482",
|
||||
"matugen_type": "scheme-tonal-spot"
|
||||
},
|
||||
"light": {
|
||||
"name": "Gruvbox Material Light Medium",
|
||||
"surface": "#fbf1c7",
|
||||
"surfaceText": "#282828",
|
||||
"surfaceVariant": "#f2e5bc",
|
||||
"surfaceVariantText": "#3c3836",
|
||||
"background": "#f2e5bc",
|
||||
"backgroundText": "#654735",
|
||||
"outline": "#bdae93",
|
||||
"surfaceContainer": "#f2e5bc",
|
||||
"surfaceContainerHigh": "#d5c4a1",
|
||||
"surfaceContainerHighest": "#bdae93",
|
||||
"primary": "#6c782e",
|
||||
"secondary": "#b47109",
|
||||
"primaryText": "#292828",
|
||||
"primaryContainer": "#535d23",
|
||||
"surfaceTint": "#45707a",
|
||||
"error": "#c14a4a",
|
||||
"warning": "#945e80",
|
||||
"info": "#6c782e",
|
||||
"matugen_type": "scheme-tonal-spot"
|
||||
}
|
||||
}
|
||||
46
docs/theme_gruvbox_material_soft.json
Normal file
46
docs/theme_gruvbox_material_soft.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"dark": {
|
||||
"name": "Gruvbox Material Dark Soft",
|
||||
"surface": "#32302f",
|
||||
"surfaceText": "#fbf1c7",
|
||||
"surfaceVariant": "#3c3836",
|
||||
"surfaceVariantText": "#ebdbb2",
|
||||
"background": "#3c3836",
|
||||
"backgroundText": "#ddc7a1",
|
||||
"outline": "#7c6f64",
|
||||
"surfaceContainer": "#3c3836",
|
||||
"surfaceContainerHigh": "#5a524c",
|
||||
"surfaceContainerHighest": "#7c6f64",
|
||||
"primary": "#a9b665",
|
||||
"secondary": "#d8a657",
|
||||
"primaryText": "#292828",
|
||||
"primaryContainer": "#798247",
|
||||
"surfaceTint": "#7daea3",
|
||||
"error": "#ea6962",
|
||||
"warning": "#d3869b",
|
||||
"info": "#89b482",
|
||||
"matugen_type": "scheme-tonal-spot"
|
||||
},
|
||||
"light": {
|
||||
"name": "Gruvbox Material Light Soft",
|
||||
"surface": "#f2e5bc",
|
||||
"surfaceText": "#282828",
|
||||
"surfaceVariant": "#ebdbb2",
|
||||
"surfaceVariantText": "#3c3836",
|
||||
"background": "#ebdbb2",
|
||||
"backgroundText": "#654735",
|
||||
"outline": "#a89984",
|
||||
"surfaceContainer": "#ebdbb2",
|
||||
"surfaceContainerHigh": "#c9b99a",
|
||||
"surfaceContainerHighest": "#a89984",
|
||||
"primary": "#6c782e",
|
||||
"secondary": "#b47109",
|
||||
"primaryText": "#292828",
|
||||
"primaryContainer": "#535d23",
|
||||
"surfaceTint": "#45707a",
|
||||
"error": "#c14a4a",
|
||||
"warning": "#945e80",
|
||||
"info": "#6c782e",
|
||||
"matugen_type": "scheme-tonal-spot"
|
||||
}
|
||||
}
|
||||
37
flake.lock
generated
37
flake.lock
generated
@@ -1,32 +1,12 @@
|
||||
{
|
||||
"nodes": {
|
||||
"dgop": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762835999,
|
||||
"narHash": "sha256-UykYGrGFOFTmDpKTLNxj1wvd1gbDG4TkqLNSbV0TYwk=",
|
||||
"owner": "AvengeMedia",
|
||||
"repo": "dgop",
|
||||
"rev": "799301991cd5dcea9b64245f9d500dcc76615653",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "AvengeMedia",
|
||||
"repo": "dgop",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1764950072,
|
||||
"narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=",
|
||||
"lastModified": 1766651565,
|
||||
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f61125a668a320878494449750330ca58b78c557",
|
||||
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -43,23 +23,22 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1764663772,
|
||||
"narHash": "sha256-sHqLmm0wAt3PC4vczJeBozI1/f4rv9yp3IjkClHDXDs=",
|
||||
"lastModified": 1766725085,
|
||||
"narHash": "sha256-O2aMFdDUYJazFrlwL7aSIHbUSEm3ADVZjmf41uBJfHs=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460",
|
||||
"revCount": 713,
|
||||
"rev": "41828c4180fb921df7992a5405f5ff05d2ac2fff",
|
||||
"revCount": 715,
|
||||
"type": "git",
|
||||
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
||||
},
|
||||
"original": {
|
||||
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460",
|
||||
"rev": "41828c4180fb921df7992a5405f5ff05d2ac2fff",
|
||||
"type": "git",
|
||||
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"dgop": "dgop",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"quickshell": "quickshell"
|
||||
}
|
||||
|
||||
30
flake.nix
30
flake.nix
@@ -3,12 +3,8 @@
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
dgop = {
|
||||
url = "github:AvengeMedia/dgop";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
quickshell = {
|
||||
url = "git+https://git.outfoxxed.me/quickshell/quickshell?rev=26531fc46ef17e9365b03770edd3fb9206fcb460";
|
||||
url = "git+https://git.outfoxxed.me/quickshell/quickshell?rev=41828c4180fb921df7992a5405f5ff05d2ac2fff";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
@@ -17,7 +13,6 @@
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
dgop,
|
||||
quickshell,
|
||||
...
|
||||
}:
|
||||
@@ -29,15 +24,14 @@
|
||||
);
|
||||
buildDmsPkgs = pkgs: {
|
||||
dms-shell = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
||||
inherit (dgop.packages.${pkgs.stdenv.hostPlatform.system}) dgop;
|
||||
quickshell = quickshell.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
||||
};
|
||||
mkModuleWithDmsPkgs =
|
||||
path:
|
||||
modulePath:
|
||||
args@{ pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
(import path (args // { dmsPkgs = buildDmsPkgs pkgs; }))
|
||||
(import modulePath (args // { dmsPkgs = buildDmsPkgs pkgs; }))
|
||||
];
|
||||
};
|
||||
mkQmlImportPath =
|
||||
@@ -145,18 +139,30 @@
|
||||
}
|
||||
);
|
||||
|
||||
quickshell = quickshell.packages.${system}.default;
|
||||
|
||||
default = self.packages.${system}.dms-shell;
|
||||
}
|
||||
);
|
||||
|
||||
homeModules.dankMaterialShell.default = mkModuleWithDmsPkgs ./distro/nix/home.nix;
|
||||
homeModules.dank-material-shell = mkModuleWithDmsPkgs ./distro/nix/home.nix;
|
||||
|
||||
homeModules.dankMaterialShell.niri = import ./distro/nix/niri.nix;
|
||||
homeModules.default = self.homeModules.dank-material-shell;
|
||||
|
||||
nixosModules.dankMaterialShell = mkModuleWithDmsPkgs ./distro/nix/nixos.nix;
|
||||
homeModules.niri = import ./distro/nix/niri.nix;
|
||||
|
||||
homeModules.dankMaterialShell.default = builtins.warn "dank-material-shell: flake output `homeModules.dankMaterialShell.default` has been renamed to `homeModules.dank-material-shell`" self.homeModules.dank-material-shell;
|
||||
|
||||
homeModules.dankMaterialShell.niri = builtins.warn "dank-material-shell: flake output `homeModules.dankMaterialShell.niri` has been renamed to `homeModules.niri`" self.homeModules.niri;
|
||||
|
||||
nixosModules.dank-material-shell = mkModuleWithDmsPkgs ./distro/nix/nixos.nix;
|
||||
|
||||
nixosModules.default = self.nixosModules.dank-material-shell;
|
||||
|
||||
nixosModules.greeter = mkModuleWithDmsPkgs ./distro/nix/greeter.nix;
|
||||
|
||||
nixosModules.dankMaterialShell = builtins.warn "dank-material-shell: flake output `nixosModules.dankMaterialShell` has been renamed to `nixosModules.dank-material-shell`" self.nixosModules.dank-material-shell;
|
||||
|
||||
devShells = forEachSystem (
|
||||
system: pkgs:
|
||||
let
|
||||
|
||||
@@ -16,6 +16,9 @@ Singleton {
|
||||
return [fullUnderscore, fullHyphen, _lang].filter(c => c && c !== "en");
|
||||
}
|
||||
|
||||
readonly property var _rtlLanguages: ["ar", "he", "iw", "fa", "ur", "ps", "sd", "dv", "yi", "ku"]
|
||||
readonly property bool isRtl: _rtlLanguages.includes(_lang)
|
||||
|
||||
readonly property url translationsFolder: Qt.resolvedUrl("../translations/poexports")
|
||||
|
||||
property string currentLocale: "en"
|
||||
|
||||
@@ -8,6 +8,9 @@ Singleton {
|
||||
id: modalManager
|
||||
|
||||
signal closeAllModalsExcept(var excludedModal)
|
||||
signal modalChanged
|
||||
|
||||
property var currentModalsByScreen: ({})
|
||||
|
||||
function openModal(modal) {
|
||||
if (!modal.allowStacking) {
|
||||
@@ -17,5 +20,17 @@ Singleton {
|
||||
PopoutManager.closeAllPopouts();
|
||||
}
|
||||
TrayMenuManager.closeAllMenus();
|
||||
|
||||
const screenName = modal.effectiveScreen?.name ?? "unknown";
|
||||
currentModalsByScreen[screenName] = modal;
|
||||
modalChanged();
|
||||
}
|
||||
|
||||
function closeModal(modal) {
|
||||
const screenName = modal.effectiveScreen?.name ?? "unknown";
|
||||
if (currentModalsByScreen[screenName] === modal) {
|
||||
delete currentModalsByScreen[screenName];
|
||||
modalChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
pragma ComponentBehavior
|
||||
|
||||
import QtCore
|
||||
import QtQuick
|
||||
@@ -14,7 +14,7 @@ import "settings/SettingsStore.js" as Store
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property int settingsConfigVersion: 3
|
||||
readonly property int settingsConfigVersion: 4
|
||||
|
||||
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
||||
|
||||
@@ -63,7 +63,9 @@ Singleton {
|
||||
property alias dankBarRightWidgetsModel: rightWidgetsModel
|
||||
|
||||
property string currentThemeName: "blue"
|
||||
property string currentThemeCategory: "generic"
|
||||
property string customThemeFile: ""
|
||||
property var registryThemeVariants: ({})
|
||||
property string matugenScheme: "scheme-tonal-spot"
|
||||
property bool runUserMatugenTemplates: true
|
||||
property string matugenTargetMonitor: ""
|
||||
@@ -72,6 +74,8 @@ Singleton {
|
||||
property string widgetBackgroundColor: "sch"
|
||||
property string widgetColorMode: "default"
|
||||
property real cornerRadius: 12
|
||||
property int niriLayoutGapsOverride: -1
|
||||
property int niriLayoutRadiusOverride: -1
|
||||
|
||||
property bool use24HourClock: true
|
||||
property bool showSeconds: false
|
||||
@@ -105,9 +109,12 @@ Singleton {
|
||||
property bool controlCenterShowNetworkIcon: true
|
||||
property bool controlCenterShowBluetoothIcon: true
|
||||
property bool controlCenterShowAudioIcon: true
|
||||
property bool controlCenterShowAudioPercent: true
|
||||
property bool controlCenterShowVpnIcon: true
|
||||
property bool controlCenterShowBrightnessIcon: false
|
||||
property bool controlCenterShowBrightnessPercent: false
|
||||
property bool controlCenterShowMicIcon: false
|
||||
property bool controlCenterShowMicPercent: true
|
||||
property bool controlCenterShowBatteryIcon: false
|
||||
property bool controlCenterShowPrinterIcon: false
|
||||
property bool showPrivacyButton: true
|
||||
@@ -165,11 +172,13 @@ Singleton {
|
||||
property int maxWorkspaceIcons: 3
|
||||
property bool workspacesPerMonitor: true
|
||||
property bool showOccupiedWorkspacesOnly: false
|
||||
property bool reverseScrolling: false
|
||||
property bool dwlShowAllTags: false
|
||||
property var workspaceNameIcons: ({})
|
||||
property bool waveProgressEnabled: true
|
||||
property bool scrollTitleEnabled: true
|
||||
property bool audioVisualizerEnabled: true
|
||||
property bool audioScrollEnabled: true
|
||||
property bool clockCompactMode: false
|
||||
property bool focusedWindowCompactMode: false
|
||||
property bool runningAppsCompactMode: true
|
||||
@@ -255,6 +264,7 @@ Singleton {
|
||||
property int batterySuspendTimeout: 0
|
||||
property int batterySuspendBehavior: SettingsData.SuspendBehavior.Suspend
|
||||
property string batteryProfileName: ""
|
||||
property int batteryChargeLimit: 100
|
||||
property bool lockBeforeSuspend: false
|
||||
property bool loginctlLockIntegration: true
|
||||
property bool fadeToLockEnabled: false
|
||||
@@ -279,9 +289,11 @@ Singleton {
|
||||
property bool matugenTemplateFirefox: true
|
||||
property bool matugenTemplatePywalfox: true
|
||||
property bool matugenTemplateVesktop: true
|
||||
property bool matugenTemplateEquibop: true
|
||||
property bool matugenTemplateGhostty: true
|
||||
property bool matugenTemplateKitty: true
|
||||
property bool matugenTemplateFoot: true
|
||||
property bool matugenTemplateNeovim: true
|
||||
property bool matugenTemplateAlacritty: true
|
||||
property bool matugenTemplateWezterm: true
|
||||
property bool matugenTemplateDgop: true
|
||||
@@ -302,6 +314,7 @@ Singleton {
|
||||
property string dockBorderColor: "surfaceText"
|
||||
property real dockBorderOpacity: 1.0
|
||||
property int dockBorderThickness: 1
|
||||
property bool dockIsolateDisplays: false
|
||||
|
||||
property bool notificationOverlayEnabled: false
|
||||
property int overviewRows: 2
|
||||
@@ -360,50 +373,279 @@ Singleton {
|
||||
property string displayNameMode: "system"
|
||||
property var screenPreferences: ({})
|
||||
property var showOnLastDisplay: ({})
|
||||
property var niriOutputSettings: ({})
|
||||
property var hyprlandOutputSettings: ({})
|
||||
|
||||
property var barConfigs: [
|
||||
{
|
||||
id: "default",
|
||||
name: "Main Bar",
|
||||
enabled: true,
|
||||
position: 0,
|
||||
screenPreferences: ["all"],
|
||||
showOnLastDisplay: true,
|
||||
leftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"],
|
||||
centerWidgets: ["music", "clock", "weather"],
|
||||
rightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
|
||||
spacing: 4,
|
||||
innerPadding: 4,
|
||||
bottomGap: 0,
|
||||
transparency: 1.0,
|
||||
widgetTransparency: 1.0,
|
||||
squareCorners: false,
|
||||
noBackground: false,
|
||||
gothCornersEnabled: false,
|
||||
gothCornerRadiusOverride: false,
|
||||
gothCornerRadiusValue: 12,
|
||||
borderEnabled: false,
|
||||
borderColor: "surfaceText",
|
||||
borderOpacity: 1.0,
|
||||
borderThickness: 1,
|
||||
widgetOutlineEnabled: false,
|
||||
widgetOutlineColor: "primary",
|
||||
widgetOutlineOpacity: 1.0,
|
||||
widgetOutlineThickness: 1,
|
||||
fontScale: 1.0,
|
||||
autoHide: false,
|
||||
autoHideDelay: 250,
|
||||
openOnOverview: false,
|
||||
visible: true,
|
||||
popupGapsAuto: true,
|
||||
popupGapsManual: 4,
|
||||
maximizeDetection: true,
|
||||
scrollEnabled: true,
|
||||
scrollXBehavior: "column",
|
||||
scrollYBehavior: "workspace"
|
||||
"id": "default",
|
||||
"name": "Main Bar",
|
||||
"enabled": true,
|
||||
"position": 0,
|
||||
"screenPreferences": ["all"],
|
||||
"showOnLastDisplay": true,
|
||||
"leftWidgets": ["launcherButton", "workspaceSwitcher", "focusedWindow"],
|
||||
"centerWidgets": ["music", "clock", "weather"],
|
||||
"rightWidgets": ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
|
||||
"spacing": 4,
|
||||
"innerPadding": 4,
|
||||
"bottomGap": 0,
|
||||
"transparency": 1.0,
|
||||
"widgetTransparency": 1.0,
|
||||
"squareCorners": false,
|
||||
"noBackground": false,
|
||||
"gothCornersEnabled": false,
|
||||
"gothCornerRadiusOverride": false,
|
||||
"gothCornerRadiusValue": 12,
|
||||
"borderEnabled": false,
|
||||
"borderColor": "surfaceText",
|
||||
"borderOpacity": 1.0,
|
||||
"borderThickness": 1,
|
||||
"widgetOutlineEnabled": false,
|
||||
"widgetOutlineColor": "primary",
|
||||
"widgetOutlineOpacity": 1.0,
|
||||
"widgetOutlineThickness": 1,
|
||||
"fontScale": 1.0,
|
||||
"autoHide": false,
|
||||
"autoHideDelay": 250,
|
||||
"showOnWindowsOpen": false,
|
||||
"openOnOverview": false,
|
||||
"visible": true,
|
||||
"popupGapsAuto": true,
|
||||
"popupGapsManual": 4,
|
||||
"maximizeDetection": true,
|
||||
"scrollEnabled": true,
|
||||
"scrollXBehavior": "column",
|
||||
"scrollYBehavior": "workspace"
|
||||
}
|
||||
]
|
||||
|
||||
property bool desktopClockEnabled: false
|
||||
property string desktopClockStyle: "analog"
|
||||
property real desktopClockTransparency: 0.8
|
||||
property string desktopClockColorMode: "primary"
|
||||
property color desktopClockCustomColor: "#ffffff"
|
||||
property bool desktopClockShowDate: true
|
||||
property bool desktopClockShowAnalogNumbers: false
|
||||
property bool desktopClockShowAnalogSeconds: true
|
||||
property real desktopClockX: -1
|
||||
property real desktopClockY: -1
|
||||
property real desktopClockWidth: 280
|
||||
property real desktopClockHeight: 180
|
||||
property var desktopClockDisplayPreferences: ["all"]
|
||||
|
||||
property bool systemMonitorEnabled: false
|
||||
property bool systemMonitorShowHeader: true
|
||||
property real systemMonitorTransparency: 0.8
|
||||
property string systemMonitorColorMode: "primary"
|
||||
property color systemMonitorCustomColor: "#ffffff"
|
||||
property bool systemMonitorShowCpu: true
|
||||
property bool systemMonitorShowCpuGraph: true
|
||||
property bool systemMonitorShowCpuTemp: true
|
||||
property bool systemMonitorShowGpuTemp: false
|
||||
property string systemMonitorGpuPciId: ""
|
||||
property bool systemMonitorShowMemory: true
|
||||
property bool systemMonitorShowMemoryGraph: true
|
||||
property bool systemMonitorShowNetwork: true
|
||||
property bool systemMonitorShowNetworkGraph: true
|
||||
property bool systemMonitorShowDisk: true
|
||||
property bool systemMonitorShowTopProcesses: false
|
||||
property int systemMonitorTopProcessCount: 3
|
||||
property string systemMonitorTopProcessSortBy: "cpu"
|
||||
property string systemMonitorLayoutMode: "auto"
|
||||
property int systemMonitorGraphInterval: 60
|
||||
property real systemMonitorX: -1
|
||||
property real systemMonitorY: -1
|
||||
property real systemMonitorWidth: 320
|
||||
property real systemMonitorHeight: 480
|
||||
property var systemMonitorDisplayPreferences: ["all"]
|
||||
property var systemMonitorVariants: []
|
||||
property var desktopWidgetPositions: ({})
|
||||
property var desktopWidgetGridSettings: ({})
|
||||
property var desktopWidgetInstances: []
|
||||
|
||||
function getDesktopWidgetGridSetting(screenKey, property, defaultValue) {
|
||||
const val = desktopWidgetGridSettings?.[screenKey]?.[property];
|
||||
return val !== undefined ? val : defaultValue;
|
||||
}
|
||||
|
||||
function setDesktopWidgetGridSetting(screenKey, property, value) {
|
||||
const allSettings = JSON.parse(JSON.stringify(desktopWidgetGridSettings || {}));
|
||||
if (!allSettings[screenKey])
|
||||
allSettings[screenKey] = {};
|
||||
allSettings[screenKey][property] = value;
|
||||
desktopWidgetGridSettings = allSettings;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getDesktopWidgetPosition(pluginId, screenKey, property, defaultValue) {
|
||||
const pos = desktopWidgetPositions?.[pluginId]?.[screenKey]?.[property];
|
||||
return pos !== undefined ? pos : defaultValue;
|
||||
}
|
||||
|
||||
function updateDesktopWidgetPosition(pluginId, screenKey, updates) {
|
||||
const allPositions = JSON.parse(JSON.stringify(desktopWidgetPositions || {}));
|
||||
if (!allPositions[pluginId])
|
||||
allPositions[pluginId] = {};
|
||||
allPositions[pluginId][screenKey] = Object.assign({}, allPositions[pluginId][screenKey] || {}, updates);
|
||||
desktopWidgetPositions = allPositions;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getSystemMonitorVariants() {
|
||||
return systemMonitorVariants || [];
|
||||
}
|
||||
|
||||
function createSystemMonitorVariant(name, config) {
|
||||
const id = "sysmon_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
||||
const variant = {
|
||||
id: id,
|
||||
name: name,
|
||||
config: config || getDefaultSystemMonitorConfig()
|
||||
};
|
||||
const variants = JSON.parse(JSON.stringify(systemMonitorVariants || []));
|
||||
variants.push(variant);
|
||||
systemMonitorVariants = variants;
|
||||
saveSettings();
|
||||
return variant;
|
||||
}
|
||||
|
||||
function updateSystemMonitorVariant(variantId, updates) {
|
||||
const variants = JSON.parse(JSON.stringify(systemMonitorVariants || []));
|
||||
const idx = variants.findIndex(v => v.id === variantId);
|
||||
if (idx === -1)
|
||||
return;
|
||||
Object.assign(variants[idx], updates);
|
||||
systemMonitorVariants = variants;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function removeSystemMonitorVariant(variantId) {
|
||||
const variants = (systemMonitorVariants || []).filter(v => v.id !== variantId);
|
||||
systemMonitorVariants = variants;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getSystemMonitorVariant(variantId) {
|
||||
return (systemMonitorVariants || []).find(v => v.id === variantId) || null;
|
||||
}
|
||||
|
||||
function getDefaultSystemMonitorConfig() {
|
||||
return {
|
||||
showHeader: true,
|
||||
transparency: 0.8,
|
||||
colorMode: "primary",
|
||||
customColor: "#ffffff",
|
||||
showCpu: true,
|
||||
showCpuGraph: true,
|
||||
showCpuTemp: true,
|
||||
showGpuTemp: false,
|
||||
gpuPciId: "",
|
||||
showMemory: true,
|
||||
showMemoryGraph: true,
|
||||
showNetwork: true,
|
||||
showNetworkGraph: true,
|
||||
showDisk: true,
|
||||
showTopProcesses: false,
|
||||
topProcessCount: 3,
|
||||
topProcessSortBy: "cpu",
|
||||
layoutMode: "auto",
|
||||
graphInterval: 60,
|
||||
x: -1,
|
||||
y: -1,
|
||||
width: 320,
|
||||
height: 480,
|
||||
displayPreferences: ["all"]
|
||||
};
|
||||
}
|
||||
|
||||
function createDesktopWidgetInstance(widgetType, name, config) {
|
||||
const id = "dw_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
||||
const instance = {
|
||||
id: id,
|
||||
widgetType: widgetType,
|
||||
name: name || widgetType,
|
||||
enabled: true,
|
||||
config: config || {},
|
||||
positions: {}
|
||||
};
|
||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
||||
instances.push(instance);
|
||||
desktopWidgetInstances = instances;
|
||||
saveSettings();
|
||||
return instance;
|
||||
}
|
||||
|
||||
function updateDesktopWidgetInstance(instanceId, updates) {
|
||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
||||
const idx = instances.findIndex(inst => inst.id === instanceId);
|
||||
if (idx === -1)
|
||||
return;
|
||||
Object.assign(instances[idx], updates);
|
||||
desktopWidgetInstances = instances;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function updateDesktopWidgetInstanceConfig(instanceId, configUpdates) {
|
||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
||||
const idx = instances.findIndex(inst => inst.id === instanceId);
|
||||
if (idx === -1)
|
||||
return;
|
||||
instances[idx].config = Object.assign({}, instances[idx].config || {}, configUpdates);
|
||||
desktopWidgetInstances = instances;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function updateDesktopWidgetInstancePosition(instanceId, screenKey, positionUpdates) {
|
||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
||||
const idx = instances.findIndex(inst => inst.id === instanceId);
|
||||
if (idx === -1)
|
||||
return;
|
||||
if (!instances[idx].positions)
|
||||
instances[idx].positions = {};
|
||||
instances[idx].positions[screenKey] = Object.assign({}, instances[idx].positions[screenKey] || {}, positionUpdates);
|
||||
desktopWidgetInstances = instances;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function removeDesktopWidgetInstance(instanceId) {
|
||||
const instances = (desktopWidgetInstances || []).filter(inst => inst.id !== instanceId);
|
||||
desktopWidgetInstances = instances;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function duplicateDesktopWidgetInstance(instanceId) {
|
||||
const source = getDesktopWidgetInstance(instanceId);
|
||||
if (!source)
|
||||
return null;
|
||||
const newId = "dw_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
||||
const instance = {
|
||||
id: newId,
|
||||
widgetType: source.widgetType,
|
||||
name: source.name + " (Copy)",
|
||||
enabled: source.enabled,
|
||||
config: JSON.parse(JSON.stringify(source.config || {})),
|
||||
positions: {}
|
||||
};
|
||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
||||
instances.push(instance);
|
||||
desktopWidgetInstances = instances;
|
||||
saveSettings();
|
||||
return instance;
|
||||
}
|
||||
|
||||
function getDesktopWidgetInstance(instanceId) {
|
||||
return (desktopWidgetInstances || []).find(inst => inst.id === instanceId) || null;
|
||||
}
|
||||
|
||||
function getDesktopWidgetInstancesOfType(widgetType) {
|
||||
return (desktopWidgetInstances || []).filter(inst => inst.widgetType === widgetType);
|
||||
}
|
||||
|
||||
function getEnabledDesktopWidgetInstances() {
|
||||
return (desktopWidgetInstances || []).filter(inst => inst.enabled);
|
||||
}
|
||||
|
||||
signal forceDankBarLayoutRefresh
|
||||
signal forceDockLayoutRefresh
|
||||
signal widgetDataChanged
|
||||
@@ -421,10 +663,12 @@ Singleton {
|
||||
|
||||
function applyStoredTheme() {
|
||||
if (typeof Theme !== "undefined") {
|
||||
Theme.currentThemeCategory = currentThemeCategory;
|
||||
Theme.switchTheme(currentThemeName, false, false);
|
||||
} else {
|
||||
Qt.callLater(function () {
|
||||
if (typeof Theme !== "undefined") {
|
||||
Theme.currentThemeCategory = currentThemeCategory;
|
||||
Theme.switchTheme(currentThemeName, false, false);
|
||||
}
|
||||
});
|
||||
@@ -458,25 +702,25 @@ Singleton {
|
||||
|
||||
const configScript = `mkdir -p ${_configDir}/gtk-3.0 ${_configDir}/gtk-4.0
|
||||
|
||||
for config_dir in ${_configDir}/gtk-3.0 ${_configDir}/gtk-4.0; do
|
||||
settings_file="$config_dir/settings.ini"
|
||||
if [ -f "$settings_file" ]; then
|
||||
for config_dir in ${_configDir}/gtk-3.0 ${_configDir}/gtk-4.0; do
|
||||
settings_file="$config_dir/settings.ini"
|
||||
if [ -f "$settings_file" ]; then
|
||||
if grep -q "^gtk-icon-theme-name=" "$settings_file"; then
|
||||
sed -i 's/^gtk-icon-theme-name=.*/gtk-icon-theme-name=${gtkThemeName}/' "$settings_file"
|
||||
sed -i 's/^gtk-icon-theme-name=.*/gtk-icon-theme-name=${gtkThemeName}/' "$settings_file"
|
||||
else
|
||||
if grep -q "\\[Settings\\]" "$settings_file"; then
|
||||
sed -i '/\\[Settings\\]/a gtk-icon-theme-name=${gtkThemeName}' "$settings_file"
|
||||
else
|
||||
echo -e '\\n[Settings]\\ngtk-icon-theme-name=${gtkThemeName}' >> "$settings_file"
|
||||
fi
|
||||
if grep -q "\\[Settings\\]" "$settings_file"; then
|
||||
sed -i '/\\[Settings\\]/a gtk-icon-theme-name=${gtkThemeName}' "$settings_file"
|
||||
else
|
||||
echo -e '\\n[Settings]\\ngtk-icon-theme-name=${gtkThemeName}' >> "$settings_file"
|
||||
fi
|
||||
else
|
||||
fi
|
||||
else
|
||||
echo -e '[Settings]\\ngtk-icon-theme-name=${gtkThemeName}' > "$settings_file"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
rm -rf ~/.cache/icon-cache ~/.cache/thumbnails 2>/dev/null || true
|
||||
pkill -HUP -f 'gtk' 2>/dev/null || true`;
|
||||
rm -rf ~/.cache/icon-cache ~/.cache/thumbnails 2>/dev/null || true
|
||||
pkill -HUP -f 'gtk' 2>/dev/null || true`;
|
||||
|
||||
Quickshell.execDetached(["sh", "-lc", configScript]);
|
||||
}
|
||||
@@ -489,36 +733,36 @@ pkill -HUP -f 'gtk' 2>/dev/null || true`;
|
||||
const qtThemeNameEscaped = qtThemeName.replace(/'/g, "'\\''");
|
||||
|
||||
const script = `mkdir -p ${_configDir}/qt5ct ${_configDir}/qt6ct ${_configDir}/environment.d 2>/dev/null || true
|
||||
update_qt_icon_theme() {
|
||||
local config_file="$1"
|
||||
local theme_name="$2"
|
||||
if [ -f "$config_file" ]; then
|
||||
if grep -q "^\\[Appearance\\]" "$config_file"; then
|
||||
if grep -q "^icon_theme=" "$config_file"; then
|
||||
update_qt_icon_theme() {
|
||||
local config_file="$1"
|
||||
local theme_name="$2"
|
||||
if [ -f "$config_file" ]; then
|
||||
if grep -q "^\\[Appearance\\]" "$config_file"; then
|
||||
if grep -q "^icon_theme=" "$config_file"; then
|
||||
sed -i "s/^icon_theme=.*/icon_theme=$theme_name/" "$config_file"
|
||||
else
|
||||
else
|
||||
sed -i "/^\\[Appearance\\]/a icon_theme=$theme_name" "$config_file"
|
||||
fi
|
||||
else
|
||||
printf "\\n[Appearance]\\nicon_theme=%s\\n" "$theme_name" >> "$config_file"
|
||||
fi
|
||||
else
|
||||
printf "[Appearance]\\nicon_theme=%s\\n" "$theme_name" > "$config_file"
|
||||
fi
|
||||
}
|
||||
update_qt_icon_theme ${_configDir}/qt5ct/qt5ct.conf '${qtThemeNameEscaped}'
|
||||
update_qt_icon_theme ${_configDir}/qt6ct/qt6ct.conf '${qtThemeNameEscaped}'
|
||||
rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || true`;
|
||||
fi
|
||||
else
|
||||
printf "\\n[Appearance]\\nicon_theme=%s\\n" "$theme_name" >> "$config_file"
|
||||
fi
|
||||
else
|
||||
printf "[Appearance]\\nicon_theme=%s\\n" "$theme_name" > "$config_file"
|
||||
fi
|
||||
}
|
||||
update_qt_icon_theme ${_configDir}/qt5ct/qt5ct.conf '${qtThemeNameEscaped}'
|
||||
update_qt_icon_theme ${_configDir}/qt6ct/qt6ct.conf '${qtThemeNameEscaped}'
|
||||
rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || true`;
|
||||
|
||||
Quickshell.execDetached(["sh", "-lc", script]);
|
||||
}
|
||||
|
||||
readonly property var _hooks: ({
|
||||
applyStoredTheme: applyStoredTheme,
|
||||
regenSystemThemes: regenSystemThemes,
|
||||
updateNiriLayout: updateNiriLayout,
|
||||
applyStoredIconTheme: applyStoredIconTheme,
|
||||
updateBarConfigs: updateBarConfigs
|
||||
"applyStoredTheme": applyStoredTheme,
|
||||
"regenSystemThemes": regenSystemThemes,
|
||||
"updateNiriLayout": updateNiriLayout,
|
||||
"applyStoredIconTheme": applyStoredIconTheme,
|
||||
"updateBarConfigs": updateBarConfigs
|
||||
})
|
||||
|
||||
function set(key, value) {
|
||||
@@ -543,7 +787,6 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
Store.parse(root, obj);
|
||||
applyStoredTheme();
|
||||
applyStoredIconTheme();
|
||||
Processes.detectIcons();
|
||||
Processes.detectQtTools();
|
||||
} catch (e) {
|
||||
console.warn("SettingsData: Failed to load settings:", e.message);
|
||||
@@ -590,7 +833,42 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
}
|
||||
|
||||
function detectAvailableIconThemes() {
|
||||
Processes.detectIcons();
|
||||
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS") || "";
|
||||
const localData = Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation));
|
||||
const homeDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.HomeLocation));
|
||||
|
||||
const dataDirs = xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat([localData]) : ["/usr/share", "/usr/local/share", localData];
|
||||
|
||||
const iconPaths = dataDirs.map(d => d + "/icons").concat([homeDir + "/.icons"]);
|
||||
const pathsArg = iconPaths.join(" ");
|
||||
|
||||
const script = `
|
||||
echo "SYSDEFAULT:$(gsettings get org.gnome.desktop.interface icon-theme 2>/dev/null | sed "s/'//g" || echo '')"
|
||||
for dir in ${pathsArg}; do
|
||||
[ -d "$dir" ] || continue
|
||||
for theme in "$dir"/*/; do
|
||||
[ -d "$theme" ] || continue
|
||||
basename "$theme"
|
||||
done
|
||||
done | grep -v '^icons$' | grep -v '^default$' | grep -v '^hicolor$' | grep -v '^locolor$' | sort -u
|
||||
`;
|
||||
|
||||
Proc.runCommand("detectIconThemes", ["sh", "-c", script], (output, exitCode) => {
|
||||
const themes = ["System Default"];
|
||||
if (output && output.trim()) {
|
||||
const lines = output.trim().split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (line.startsWith("SYSDEFAULT:")) {
|
||||
systemDefaultIconTheme = line.substring(11).trim();
|
||||
continue;
|
||||
}
|
||||
if (line)
|
||||
themes.push(line);
|
||||
}
|
||||
}
|
||||
availableIconThemes = themes;
|
||||
});
|
||||
}
|
||||
|
||||
function getEffectiveTimeFormat() {
|
||||
@@ -654,15 +932,14 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
return barHeight + spacing + bottomGap - gothOffset + Theme.popupDistance;
|
||||
}
|
||||
|
||||
function getPopupTriggerPosition(globalPos, screen, barThickness, widgetWidth, barSpacing, barPosition, barConfig) {
|
||||
const screenX = screen ? screen.x : 0;
|
||||
const screenY = screen ? screen.y : 0;
|
||||
const relativeX = globalPos.x - screenX;
|
||||
const relativeY = globalPos.y - screenY;
|
||||
function getPopupTriggerPosition(pos, screen, barThickness, widgetWidth, barSpacing, barPosition, barConfig) {
|
||||
const relativeX = pos.x;
|
||||
const relativeY = pos.y;
|
||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||
const spacing = barSpacing !== undefined ? barSpacing : (defaultBar?.spacing ?? 4);
|
||||
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
|
||||
const bottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
|
||||
const rawBottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
|
||||
const bottomGap = Math.max(0, rawBottomGap);
|
||||
|
||||
const useAutoGaps = (barConfig && barConfig.popupGapsAuto !== undefined) ? barConfig.popupGapsAuto : (defaultBar?.popupGapsAuto ?? true);
|
||||
const manualGapValue = (barConfig && barConfig.popupGapsManual !== undefined) ? barConfig.popupGapsManual : (defaultBar?.popupGapsManual ?? 4);
|
||||
@@ -723,7 +1000,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
let leftBar = 0;
|
||||
let rightBar = 0;
|
||||
|
||||
for (let i = 0; i < enabledBars.length; i++) {
|
||||
for (var i = 0; i < enabledBars.length; i++) {
|
||||
const other = enabledBars[i];
|
||||
if (other.id === barConfig.id)
|
||||
continue;
|
||||
@@ -793,7 +1070,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
|
||||
if (barConfig) {
|
||||
const enabledBars = getEnabledBarConfigs();
|
||||
for (let i = 0; i < enabledBars.length; i++) {
|
||||
for (var i = 0; i < enabledBars.length; i++) {
|
||||
const other = enabledBars[i];
|
||||
if (other.id === barConfig.id)
|
||||
continue;
|
||||
@@ -913,7 +1190,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
updateBarConfigs();
|
||||
|
||||
if (positionChanged) {
|
||||
NotificationService.clearAllPopups();
|
||||
NotificationService.dismissAllPopups();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -925,7 +1202,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const conflicts = [];
|
||||
const enabledBars = getEnabledBarConfigs();
|
||||
|
||||
for (let i = 0; i < enabledBars.length; i++) {
|
||||
for (var i = 0; i < enabledBars.length; i++) {
|
||||
const other = enabledBars[i];
|
||||
if (other.id === barId)
|
||||
continue;
|
||||
@@ -938,9 +1215,9 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const hasAll = barScreens.includes("all") || otherScreens.includes("all");
|
||||
if (hasAll) {
|
||||
conflicts.push({
|
||||
barId: other.id,
|
||||
barName: other.name,
|
||||
reason: "Same position on all screens"
|
||||
"barId": other.id,
|
||||
"barName": other.name,
|
||||
"reason": "Same position on all screens"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -948,9 +1225,9 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const overlapping = barScreens.some(screen => otherScreens.includes(screen));
|
||||
if (overlapping) {
|
||||
conflicts.push({
|
||||
barId: other.id,
|
||||
barName: other.name,
|
||||
reason: "Same position on overlapping screens"
|
||||
"barId": other.id,
|
||||
"barName": other.name,
|
||||
"reason": "Same position on overlapping screens"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -972,7 +1249,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
|
||||
function getScreensSortedByPosition() {
|
||||
const screens = [];
|
||||
for (let i = 0; i < Quickshell.screens.length; i++) {
|
||||
for (var i = 0; i < Quickshell.screens.length; i++) {
|
||||
screens.push(Quickshell.screens[i]);
|
||||
}
|
||||
screens.sort((a, b) => {
|
||||
@@ -989,7 +1266,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const sorted = getScreensSortedByPosition();
|
||||
let modelCount = 0;
|
||||
let screenIndex = -1;
|
||||
for (let i = 0; i < sorted.length; i++) {
|
||||
for (var i = 0; i < sorted.length; i++) {
|
||||
if (sorted[i].model === screen.model) {
|
||||
if (sorted[i].name === screen.name) {
|
||||
screenIndex = modelCount;
|
||||
@@ -1058,7 +1335,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
}
|
||||
|
||||
function sendTestNotifications() {
|
||||
NotificationService.clearAllPopups();
|
||||
NotificationService.dismissAllPopups();
|
||||
sendTestNotification(0);
|
||||
testNotifTimer1.start();
|
||||
testNotifTimer2.start();
|
||||
@@ -1187,7 +1464,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||
if (defaultBar) {
|
||||
updateBarConfig(defaultBar.id, {
|
||||
spacing: spacing
|
||||
"spacing": spacing
|
||||
});
|
||||
}
|
||||
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
|
||||
@@ -1216,7 +1493,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
return;
|
||||
}
|
||||
updateBarConfig(defaultBar.id, {
|
||||
position: position
|
||||
"position": position
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1224,7 +1501,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||
if (defaultBar) {
|
||||
updateBarConfig(defaultBar.id, {
|
||||
leftWidgets: order
|
||||
"leftWidgets": order
|
||||
});
|
||||
updateListModel(leftWidgetsModel, order);
|
||||
}
|
||||
@@ -1234,7 +1511,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||
if (defaultBar) {
|
||||
updateBarConfig(defaultBar.id, {
|
||||
centerWidgets: order
|
||||
"centerWidgets": order
|
||||
});
|
||||
updateListModel(centerWidgetsModel, order);
|
||||
}
|
||||
@@ -1244,7 +1521,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||
if (defaultBar) {
|
||||
updateBarConfig(defaultBar.id, {
|
||||
rightWidgets: order
|
||||
"rightWidgets": order
|
||||
});
|
||||
updateListModel(rightWidgetsModel, order);
|
||||
}
|
||||
@@ -1257,9 +1534,9 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||
if (defaultBar) {
|
||||
updateBarConfig(defaultBar.id, {
|
||||
leftWidgets: defaultLeft,
|
||||
centerWidgets: defaultCenter,
|
||||
rightWidgets: defaultRight
|
||||
"leftWidgets": defaultLeft,
|
||||
"centerWidgets": defaultCenter,
|
||||
"rightWidgets": defaultRight
|
||||
});
|
||||
}
|
||||
updateListModel(leftWidgetsModel, defaultLeft);
|
||||
@@ -1303,11 +1580,46 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
return workspaceNameIcons[workspaceName] || null;
|
||||
}
|
||||
|
||||
function getRegistryThemeVariant(themeId, defaultVariant) {
|
||||
var stored = registryThemeVariants[themeId];
|
||||
if (typeof stored === "string")
|
||||
return stored || defaultVariant || "";
|
||||
return defaultVariant || "";
|
||||
}
|
||||
|
||||
function setRegistryThemeVariant(themeId, variantId) {
|
||||
var variants = JSON.parse(JSON.stringify(registryThemeVariants));
|
||||
variants[themeId] = variantId;
|
||||
registryThemeVariants = variants;
|
||||
saveSettings();
|
||||
if (typeof Theme !== "undefined")
|
||||
Theme.reloadCustomThemeVariant();
|
||||
}
|
||||
|
||||
function getRegistryThemeMultiVariant(themeId, defaults) {
|
||||
var stored = registryThemeVariants[themeId];
|
||||
if (stored && typeof stored === "object")
|
||||
return stored;
|
||||
return defaults || {};
|
||||
}
|
||||
|
||||
function setRegistryThemeMultiVariant(themeId, flavor, accent) {
|
||||
var variants = JSON.parse(JSON.stringify(registryThemeVariants));
|
||||
variants[themeId] = {
|
||||
flavor: flavor,
|
||||
accent: accent
|
||||
};
|
||||
registryThemeVariants = variants;
|
||||
saveSettings();
|
||||
if (typeof Theme !== "undefined")
|
||||
Theme.reloadCustomThemeVariant();
|
||||
}
|
||||
|
||||
function toggleDankBarVisible() {
|
||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||
if (defaultBar) {
|
||||
updateBarConfig(defaultBar.id, {
|
||||
visible: !defaultBar.visible
|
||||
"visible": !defaultBar.visible
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1345,6 +1657,87 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
return settings ? JSON.parse(JSON.stringify(settings)) : {};
|
||||
}
|
||||
|
||||
function getNiriOutputSetting(outputId, key, defaultValue) {
|
||||
if (!niriOutputSettings[outputId])
|
||||
return defaultValue;
|
||||
return niriOutputSettings[outputId][key] !== undefined ? niriOutputSettings[outputId][key] : defaultValue;
|
||||
}
|
||||
|
||||
function setNiriOutputSetting(outputId, key, value) {
|
||||
const updated = JSON.parse(JSON.stringify(niriOutputSettings));
|
||||
if (!updated[outputId])
|
||||
updated[outputId] = {};
|
||||
updated[outputId][key] = value;
|
||||
niriOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getNiriOutputSettings(outputId) {
|
||||
const settings = niriOutputSettings[outputId];
|
||||
return settings ? JSON.parse(JSON.stringify(settings)) : {};
|
||||
}
|
||||
|
||||
function setNiriOutputSettings(outputId, settings) {
|
||||
const updated = JSON.parse(JSON.stringify(niriOutputSettings));
|
||||
updated[outputId] = settings;
|
||||
niriOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function removeNiriOutputSettings(outputId) {
|
||||
if (!niriOutputSettings[outputId])
|
||||
return;
|
||||
const updated = JSON.parse(JSON.stringify(niriOutputSettings));
|
||||
delete updated[outputId];
|
||||
niriOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getHyprlandOutputSetting(outputId, key, defaultValue) {
|
||||
if (!hyprlandOutputSettings[outputId])
|
||||
return defaultValue;
|
||||
return hyprlandOutputSettings[outputId][key] !== undefined ? hyprlandOutputSettings[outputId][key] : defaultValue;
|
||||
}
|
||||
|
||||
function setHyprlandOutputSetting(outputId, key, value) {
|
||||
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
||||
if (!updated[outputId])
|
||||
updated[outputId] = {};
|
||||
updated[outputId][key] = value;
|
||||
hyprlandOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function removeHyprlandOutputSetting(outputId, key) {
|
||||
if (!hyprlandOutputSettings[outputId] || !(key in hyprlandOutputSettings[outputId]))
|
||||
return;
|
||||
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
||||
delete updated[outputId][key];
|
||||
hyprlandOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getHyprlandOutputSettings(outputId) {
|
||||
const settings = hyprlandOutputSettings[outputId];
|
||||
return settings ? JSON.parse(JSON.stringify(settings)) : {};
|
||||
}
|
||||
|
||||
function setHyprlandOutputSettings(outputId, settings) {
|
||||
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
||||
updated[outputId] = settings;
|
||||
hyprlandOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function removeHyprlandOutputSettings(outputId) {
|
||||
if (!hyprlandOutputSettings[outputId])
|
||||
return;
|
||||
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
||||
delete updated[outputId];
|
||||
hyprlandOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: leftWidgetsModel
|
||||
}
|
||||
@@ -1396,6 +1789,8 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
||||
const txt = settingsFile.text();
|
||||
const obj = (txt && txt.trim()) ? JSON.parse(txt) : null;
|
||||
Store.parse(root, obj);
|
||||
applyStoredTheme();
|
||||
applyStoredIconTheme();
|
||||
} catch (e) {
|
||||
console.warn("SettingsData: Failed to reload settings:", e.message);
|
||||
}
|
||||
|
||||
@@ -1,545 +1,430 @@
|
||||
// Stock theme definitions for DankMaterialShell
|
||||
// Separated from Theme.qml to keep that file clean
|
||||
|
||||
const CatppuccinMocha = {
|
||||
surface: "#181825",
|
||||
surfaceText: "#cdd6f4",
|
||||
surfaceVariant: "#1e1e2e",
|
||||
surfaceVariantText: "#a6adc8",
|
||||
background: "#181825",
|
||||
backgroundText: "#cdd6f4",
|
||||
outline: "#6c7086",
|
||||
surfaceContainer: "#1e1e2e",
|
||||
surfaceContainerHigh: "#313244",
|
||||
surfaceContainerHighest: "#45475a"
|
||||
}
|
||||
|
||||
const CatppuccinLatte = {
|
||||
surface: "#e6e9ef",
|
||||
surfaceText: "#4c4f69",
|
||||
surfaceVariant: "#e6e9ef",
|
||||
surfaceVariantText: "#6c6f85",
|
||||
background: "#eff1f5",
|
||||
backgroundText: "#4c4f69",
|
||||
outline: "#9ca0b0",
|
||||
surfaceContainer: "#dce0e8",
|
||||
surfaceContainerHigh: "#ccd0da",
|
||||
surfaceContainerHighest: "#bcc0cc"
|
||||
}
|
||||
|
||||
const CatppuccinVariants = {
|
||||
"cat-rosewater": {
|
||||
name: "Rosewater",
|
||||
dark: { primary: "#f5e0dc", secondary: "#f2cdcd", primaryText: "#1e1e2e", primaryContainer: "#7d5d56", surfaceTint: "#f5e0dc" },
|
||||
light: { primary: "#dc8a78", secondary: "#dd7878", primaryText: "#ffffff", primaryContainer: "#f6e7e3", surfaceTint: "#dc8a78" }
|
||||
},
|
||||
"cat-flamingo": {
|
||||
name: "Flamingo",
|
||||
dark: { primary: "#f2cdcd", secondary: "#f5e0dc", primaryText: "#1e1e2e", primaryContainer: "#7a555a", surfaceTint: "#f2cdcd" },
|
||||
light: { primary: "#dd7878", secondary: "#dc8a78", primaryText: "#ffffff", primaryContainer: "#f6e5e5", surfaceTint: "#dd7878" }
|
||||
},
|
||||
"cat-pink": {
|
||||
name: "Pink",
|
||||
dark: { primary: "#f5c2e7", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#7a3f69", surfaceTint: "#f5c2e7" },
|
||||
light: { primary: "#ea76cb", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#f7d7ee", surfaceTint: "#ea76cb" }
|
||||
},
|
||||
"cat-mauve": {
|
||||
name: "Mauve",
|
||||
dark: { primary: "#cba6f7", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#55307f", surfaceTint: "#cba6f7" },
|
||||
light: { primary: "#8839ef", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#eadcff", surfaceTint: "#8839ef" }
|
||||
},
|
||||
"cat-red": {
|
||||
name: "Red",
|
||||
dark: { primary: "#f38ba8", secondary: "#eba0ac", primaryText: "#1e1e2e", primaryContainer: "#6f2438", surfaceTint: "#f38ba8" },
|
||||
light: { primary: "#d20f39", secondary: "#e64553", primaryText: "#ffffff", primaryContainer: "#f6d0d6", surfaceTint: "#d20f39" }
|
||||
},
|
||||
"cat-maroon": {
|
||||
name: "Maroon",
|
||||
dark: { primary: "#eba0ac", secondary: "#f38ba8", primaryText: "#1e1e2e", primaryContainer: "#6d3641", surfaceTint: "#eba0ac" },
|
||||
light: { primary: "#e64553", secondary: "#d20f39", primaryText: "#ffffff", primaryContainer: "#f7d8dc", surfaceTint: "#e64553" }
|
||||
},
|
||||
"cat-peach": {
|
||||
name: "Peach",
|
||||
dark: { primary: "#fab387", secondary: "#f9e2af", primaryText: "#1e1e2e", primaryContainer: "#734226", surfaceTint: "#fab387" },
|
||||
light: { primary: "#fe640b", secondary: "#df8e1d", primaryText: "#ffffff", primaryContainer: "#ffe4d5", surfaceTint: "#fe640b" }
|
||||
},
|
||||
"cat-yellow": {
|
||||
name: "Yellow",
|
||||
dark: { primary: "#f9e2af", secondary: "#a6e3a1", primaryText: "#1e1e2e", primaryContainer: "#6e5a2f", surfaceTint: "#f9e2af" },
|
||||
light: { primary: "#df8e1d", secondary: "#40a02b", primaryText: "#ffffff", primaryContainer: "#fff6d6", surfaceTint: "#df8e1d" }
|
||||
},
|
||||
"cat-green": {
|
||||
name: "Green",
|
||||
dark: { primary: "#a6e3a1", secondary: "#94e2d5", primaryText: "#1e1e2e", primaryContainer: "#2f5f36", surfaceTint: "#a6e3a1" },
|
||||
light: { primary: "#40a02b", secondary: "#179299", primaryText: "#ffffff", primaryContainer: "#dff4e0", surfaceTint: "#40a02b" }
|
||||
},
|
||||
"cat-teal": {
|
||||
name: "Teal",
|
||||
dark: { primary: "#94e2d5", secondary: "#89dceb", primaryText: "#1e1e2e", primaryContainer: "#2e5e59", surfaceTint: "#94e2d5" },
|
||||
light: { primary: "#179299", secondary: "#04a5e5", primaryText: "#ffffff", primaryContainer: "#daf3f1", surfaceTint: "#179299" }
|
||||
},
|
||||
"cat-sky": {
|
||||
name: "Sky",
|
||||
dark: { primary: "#89dceb", secondary: "#74c7ec", primaryText: "#1e1e2e", primaryContainer: "#24586a", surfaceTint: "#89dceb" },
|
||||
light: { primary: "#04a5e5", secondary: "#209fb5", primaryText: "#ffffff", primaryContainer: "#dbf1fb", surfaceTint: "#04a5e5" }
|
||||
},
|
||||
"cat-sapphire": {
|
||||
name: "Sapphire",
|
||||
dark: { primary: "#74c7ec", secondary: "#89b4fa", primaryText: "#1e1e2e", primaryContainer: "#1f4d6f", surfaceTint: "#74c7ec" },
|
||||
light: { primary: "#209fb5", secondary: "#1e66f5", primaryText: "#ffffff", primaryContainer: "#def3f8", surfaceTint: "#209fb5" }
|
||||
},
|
||||
"cat-blue": {
|
||||
name: "Blue",
|
||||
dark: { primary: "#89b4fa", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#243f75", surfaceTint: "#89b4fa" },
|
||||
light: { primary: "#1e66f5", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#e0e9ff", surfaceTint: "#1e66f5" }
|
||||
},
|
||||
"cat-lavender": {
|
||||
name: "Lavender",
|
||||
dark: { primary: "#b4befe", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#3f4481", surfaceTint: "#b4befe" },
|
||||
light: { primary: "#7287fd", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#e5e8ff", surfaceTint: "#7287fd" }
|
||||
}
|
||||
}
|
||||
|
||||
function getCatppuccinTheme(variant, isLight = false) {
|
||||
const variantData = CatppuccinVariants[variant]
|
||||
if (!variantData) return null
|
||||
|
||||
const baseColors = isLight ? CatppuccinLatte : CatppuccinMocha
|
||||
const accentColors = isLight ? variantData.light : variantData.dark
|
||||
|
||||
return Object.assign({
|
||||
name: `${variantData.name}${isLight ? ' Light' : ''}`
|
||||
}, baseColors, accentColors)
|
||||
}
|
||||
|
||||
const StockThemes = {
|
||||
DARK: {
|
||||
blue: {
|
||||
name: "Blue",
|
||||
primary: "#42a5f5",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#0d47a1",
|
||||
secondary: "#8ab4f8",
|
||||
surface: "#101418",
|
||||
surfaceText: "#e0e2e8",
|
||||
surfaceVariant: "#42474e",
|
||||
surfaceVariantText: "#c2c7cf",
|
||||
surfaceTint: "#8ab4f8",
|
||||
background: "#101418",
|
||||
backgroundText: "#e0e2e8",
|
||||
outline: "#8c9199",
|
||||
surfaceContainer: "#1d2024",
|
||||
surfaceContainerHigh: "#272a2f",
|
||||
surfaceContainerHighest: "#32353a"
|
||||
},
|
||||
purple: {
|
||||
name: "Purple",
|
||||
primary: "#D0BCFF",
|
||||
primaryText: "#381E72",
|
||||
primaryContainer: "#4F378B",
|
||||
secondary: "#CCC2DC",
|
||||
surface: "#141218",
|
||||
surfaceText: "#e6e0e9",
|
||||
surfaceVariant: "#49454e",
|
||||
surfaceVariantText: "#cac4cf",
|
||||
surfaceTint: "#D0BCFF",
|
||||
background: "#141218",
|
||||
backgroundText: "#e6e0e9",
|
||||
outline: "#948f99",
|
||||
surfaceContainer: "#211f24",
|
||||
surfaceContainerHigh: "#2b292f",
|
||||
surfaceContainerHighest: "#36343a"
|
||||
},
|
||||
green: {
|
||||
name: "Green",
|
||||
primary: "#4caf50",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#1b5e20",
|
||||
secondary: "#81c995",
|
||||
surface: "#10140f",
|
||||
surfaceText: "#e0e4db",
|
||||
surfaceVariant: "#424940",
|
||||
surfaceVariantText: "#c2c9bd",
|
||||
surfaceTint: "#81c995",
|
||||
background: "#10140f",
|
||||
backgroundText: "#e0e4db",
|
||||
outline: "#8c9388",
|
||||
surfaceContainer: "#1d211b",
|
||||
surfaceContainerHigh: "#272b25",
|
||||
surfaceContainerHighest: "#323630"
|
||||
},
|
||||
orange: {
|
||||
name: "Orange",
|
||||
primary: "#ff6d00",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#3e2723",
|
||||
secondary: "#ffb74d",
|
||||
surface: "#1a120e",
|
||||
surfaceText: "#f0dfd8",
|
||||
surfaceVariant: "#52443d",
|
||||
surfaceVariantText: "#d7c2b9",
|
||||
surfaceTint: "#ffb74d",
|
||||
background: "#1a120e",
|
||||
backgroundText: "#f0dfd8",
|
||||
outline: "#a08d85",
|
||||
surfaceContainer: "#271e1a",
|
||||
surfaceContainerHigh: "#322824",
|
||||
surfaceContainerHighest: "#3d332e"
|
||||
},
|
||||
red: {
|
||||
name: "Red",
|
||||
primary: "#f44336",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#4a0e0e",
|
||||
secondary: "#f28b82",
|
||||
surface: "#1a1110",
|
||||
surfaceText: "#f1dedc",
|
||||
surfaceVariant: "#534341",
|
||||
surfaceVariantText: "#d8c2be",
|
||||
surfaceTint: "#f28b82",
|
||||
background: "#1a1110",
|
||||
backgroundText: "#f1dedc",
|
||||
outline: "#a08c89",
|
||||
surfaceContainer: "#271d1c",
|
||||
surfaceContainerHigh: "#322826",
|
||||
surfaceContainerHighest: "#3d3231"
|
||||
},
|
||||
cyan: {
|
||||
name: "Cyan",
|
||||
primary: "#00bcd4",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#004d5c",
|
||||
secondary: "#4dd0e1",
|
||||
surface: "#0e1416",
|
||||
surfaceText: "#dee3e5",
|
||||
surfaceVariant: "#3f484a",
|
||||
surfaceVariantText: "#bfc8ca",
|
||||
surfaceTint: "#4dd0e1",
|
||||
background: "#0e1416",
|
||||
backgroundText: "#dee3e5",
|
||||
outline: "#899295",
|
||||
surfaceContainer: "#1b2122",
|
||||
surfaceContainerHigh: "#252b2c",
|
||||
surfaceContainerHighest: "#303637"
|
||||
},
|
||||
pink: {
|
||||
name: "Pink",
|
||||
primary: "#e91e63",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#4a0e2f",
|
||||
secondary: "#f8bbd9",
|
||||
surface: "#191112",
|
||||
surfaceText: "#f0dee0",
|
||||
surfaceVariant: "#524345",
|
||||
surfaceVariantText: "#d6c2c3",
|
||||
surfaceTint: "#f8bbd9",
|
||||
background: "#191112",
|
||||
backgroundText: "#f0dee0",
|
||||
outline: "#9f8c8e",
|
||||
surfaceContainer: "#261d1e",
|
||||
surfaceContainerHigh: "#312829",
|
||||
surfaceContainerHighest: "#3c3233"
|
||||
},
|
||||
amber: {
|
||||
name: "Amber",
|
||||
primary: "#ffc107",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#4a3c00",
|
||||
secondary: "#ffd54f",
|
||||
surface: "#17130b",
|
||||
surfaceText: "#ebe1d4",
|
||||
surfaceVariant: "#4d4639",
|
||||
surfaceVariantText: "#d0c5b4",
|
||||
surfaceTint: "#ffd54f",
|
||||
background: "#17130b",
|
||||
backgroundText: "#ebe1d4",
|
||||
outline: "#998f80",
|
||||
surfaceContainer: "#231f17",
|
||||
surfaceContainerHigh: "#2e2921",
|
||||
surfaceContainerHighest: "#39342b"
|
||||
},
|
||||
coral: {
|
||||
name: "Coral",
|
||||
primary: "#ffb4ab",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#8c1d18",
|
||||
secondary: "#f9dedc",
|
||||
surface: "#1a1110",
|
||||
surfaceText: "#f1dedc",
|
||||
surfaceVariant: "#534341",
|
||||
surfaceVariantText: "#d8c2bf",
|
||||
surfaceTint: "#ffb4ab",
|
||||
background: "#1a1110",
|
||||
backgroundText: "#f1dedc",
|
||||
outline: "#a08c8a",
|
||||
surfaceContainer: "#271d1c",
|
||||
surfaceContainerHigh: "#322826",
|
||||
surfaceContainerHighest: "#3d3231"
|
||||
},
|
||||
monochrome: {
|
||||
name: "Monochrome",
|
||||
primary: "#ffffff",
|
||||
primaryText: "#2b303c",
|
||||
primaryContainer: "#424753",
|
||||
secondary: "#c4c6d0",
|
||||
surface: "#2a2a2a",
|
||||
surfaceText: "#e4e2e3",
|
||||
surfaceVariant: "#474648",
|
||||
surfaceVariantText: "#c8c6c7",
|
||||
surfaceTint: "#c2c6d6",
|
||||
background: "#131315",
|
||||
backgroundText: "#e4e2e3",
|
||||
outline: "#929092",
|
||||
surfaceContainer: "#353535",
|
||||
surfaceContainerHigh: "#424242",
|
||||
surfaceContainerHighest: "#505050",
|
||||
error: "#ffb4ab",
|
||||
warning: "#3f4759",
|
||||
info: "#595e6c",
|
||||
matugen_type: "scheme-monochrome"
|
||||
}
|
||||
DARK: {
|
||||
blue: {
|
||||
name: "Blue",
|
||||
primary: "#42a5f5",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#0d47a1",
|
||||
secondary: "#8ab4f8",
|
||||
surface: "#101418",
|
||||
surfaceText: "#e0e2e8",
|
||||
surfaceVariant: "#42474e",
|
||||
surfaceVariantText: "#c2c7cf",
|
||||
surfaceTint: "#8ab4f8",
|
||||
background: "#101418",
|
||||
backgroundText: "#e0e2e8",
|
||||
outline: "#8c9199",
|
||||
surfaceContainer: "#1d2024",
|
||||
surfaceContainerHigh: "#272a2f",
|
||||
surfaceContainerHighest: "#32353a",
|
||||
},
|
||||
LIGHT: {
|
||||
blue: {
|
||||
name: "Blue Light",
|
||||
primary: "#1976d2",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e3f2fd",
|
||||
secondary: "#42a5f5",
|
||||
surface: "#f7f9ff",
|
||||
surfaceText: "#181c20",
|
||||
surfaceVariant: "#dee3eb",
|
||||
surfaceVariantText: "#42474e",
|
||||
surfaceTint: "#1976d2",
|
||||
background: "#f7f9ff",
|
||||
backgroundText: "#181c20",
|
||||
outline: "#72777f",
|
||||
surfaceContainer: "#eceef4",
|
||||
surfaceContainerHigh: "#e6e8ee",
|
||||
surfaceContainerHighest: "#e0e2e8"
|
||||
},
|
||||
purple: {
|
||||
name: "Purple Light",
|
||||
primary: "#6750A4",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#EADDFF",
|
||||
secondary: "#625B71",
|
||||
surface: "#fef7ff",
|
||||
surfaceText: "#1d1b20",
|
||||
surfaceVariant: "#e7e0eb",
|
||||
surfaceVariantText: "#49454e",
|
||||
surfaceTint: "#6750A4",
|
||||
background: "#fef7ff",
|
||||
backgroundText: "#1d1b20",
|
||||
outline: "#7a757f",
|
||||
surfaceContainer: "#f2ecf4",
|
||||
surfaceContainerHigh: "#ece6ee",
|
||||
surfaceContainerHighest: "#e6e0e9"
|
||||
},
|
||||
green: {
|
||||
name: "Green Light",
|
||||
primary: "#2e7d32",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e8f5e8",
|
||||
secondary: "#4caf50",
|
||||
surface: "#f7fbf1",
|
||||
surfaceText: "#191d17",
|
||||
surfaceVariant: "#dee5d8",
|
||||
surfaceVariantText: "#424940",
|
||||
surfaceTint: "#2e7d32",
|
||||
background: "#f7fbf1",
|
||||
backgroundText: "#191d17",
|
||||
outline: "#72796f",
|
||||
surfaceContainer: "#ecefe6",
|
||||
surfaceContainerHigh: "#e6e9e0",
|
||||
surfaceContainerHighest: "#e0e4db"
|
||||
},
|
||||
orange: {
|
||||
name: "Orange Light",
|
||||
primary: "#e65100",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#ffecb3",
|
||||
secondary: "#ff9800",
|
||||
surface: "#fff8f6",
|
||||
surfaceText: "#221a16",
|
||||
surfaceVariant: "#f4ded5",
|
||||
surfaceVariantText: "#52443d",
|
||||
surfaceTint: "#e65100",
|
||||
background: "#fff8f6",
|
||||
backgroundText: "#221a16",
|
||||
outline: "#85736c",
|
||||
surfaceContainer: "#fceae3",
|
||||
surfaceContainerHigh: "#f6e5de",
|
||||
surfaceContainerHighest: "#f0dfd8"
|
||||
},
|
||||
red: {
|
||||
name: "Red Light",
|
||||
primary: "#d32f2f",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#ffebee",
|
||||
secondary: "#f44336",
|
||||
surface: "#fff8f7",
|
||||
surfaceText: "#231918",
|
||||
surfaceVariant: "#f5ddda",
|
||||
surfaceVariantText: "#534341",
|
||||
surfaceTint: "#d32f2f",
|
||||
background: "#fff8f7",
|
||||
backgroundText: "#231918",
|
||||
outline: "#857370",
|
||||
surfaceContainer: "#fceae7",
|
||||
surfaceContainerHigh: "#f7e4e1",
|
||||
surfaceContainerHighest: "#f1dedc"
|
||||
},
|
||||
cyan: {
|
||||
name: "Cyan Light",
|
||||
primary: "#0097a7",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e0f2f1",
|
||||
secondary: "#00bcd4",
|
||||
surface: "#f5fafc",
|
||||
surfaceText: "#171d1e",
|
||||
surfaceVariant: "#dbe4e6",
|
||||
surfaceVariantText: "#3f484a",
|
||||
surfaceTint: "#0097a7",
|
||||
background: "#f5fafc",
|
||||
backgroundText: "#171d1e",
|
||||
outline: "#6f797b",
|
||||
surfaceContainer: "#e9eff0",
|
||||
surfaceContainerHigh: "#e3e9eb",
|
||||
surfaceContainerHighest: "#dee3e5"
|
||||
},
|
||||
pink: {
|
||||
name: "Pink Light",
|
||||
primary: "#c2185b",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#fce4ec",
|
||||
secondary: "#e91e63",
|
||||
surface: "#fff8f7",
|
||||
surfaceText: "#22191a",
|
||||
surfaceVariant: "#f3dddf",
|
||||
surfaceVariantText: "#524345",
|
||||
surfaceTint: "#c2185b",
|
||||
background: "#fff8f7",
|
||||
backgroundText: "#22191a",
|
||||
outline: "#847375",
|
||||
surfaceContainer: "#fbeaeb",
|
||||
surfaceContainerHigh: "#f5e4e5",
|
||||
surfaceContainerHighest: "#f0dee0"
|
||||
},
|
||||
amber: {
|
||||
name: "Amber Light",
|
||||
primary: "#ff8f00",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#fff8e1",
|
||||
secondary: "#ffc107",
|
||||
surface: "#fff8f2",
|
||||
surfaceText: "#1f1b13",
|
||||
surfaceVariant: "#ede1cf",
|
||||
surfaceVariantText: "#4d4639",
|
||||
surfaceTint: "#ff8f00",
|
||||
background: "#fff8f2",
|
||||
backgroundText: "#1f1b13",
|
||||
outline: "#7f7667",
|
||||
surfaceContainer: "#f6ecdf",
|
||||
surfaceContainerHigh: "#f1e7d9",
|
||||
surfaceContainerHighest: "#ebe1d4"
|
||||
},
|
||||
coral: {
|
||||
name: "Coral Light",
|
||||
primary: "#8c1d18",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#ffdad6",
|
||||
secondary: "#ff5449",
|
||||
surface: "#fff8f7",
|
||||
surfaceText: "#231918",
|
||||
surfaceVariant: "#f5ddda",
|
||||
surfaceVariantText: "#534341",
|
||||
surfaceTint: "#8c1d18",
|
||||
background: "#fff8f7",
|
||||
backgroundText: "#231918",
|
||||
outline: "#857371",
|
||||
surfaceContainer: "#fceae7",
|
||||
surfaceContainerHigh: "#f6e4e2",
|
||||
surfaceContainerHighest: "#f1dedc"
|
||||
},
|
||||
monochrome: {
|
||||
name: "Monochrome Light",
|
||||
primary: "#2b303c",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#d6d7dc",
|
||||
secondary: "#4a4d56",
|
||||
surface: "#f5f5f6",
|
||||
surfaceText: "#2a2a2a",
|
||||
surfaceVariant: "#e0e0e2",
|
||||
surfaceVariantText: "#424242",
|
||||
surfaceTint: "#5a5f6e",
|
||||
background: "#ffffff",
|
||||
backgroundText: "#1a1a1a",
|
||||
outline: "#757577",
|
||||
surfaceContainer: "#e8e8ea",
|
||||
surfaceContainerHigh: "#dcdcde",
|
||||
surfaceContainerHighest: "#d0d0d2",
|
||||
error: "#ba1a1a",
|
||||
warning: "#f9e79f",
|
||||
info: "#5d6475",
|
||||
matugen_type: "scheme-monochrome"
|
||||
}
|
||||
}
|
||||
}
|
||||
purple: {
|
||||
name: "Purple",
|
||||
primary: "#D0BCFF",
|
||||
primaryText: "#381E72",
|
||||
primaryContainer: "#4F378B",
|
||||
secondary: "#CCC2DC",
|
||||
surface: "#141218",
|
||||
surfaceText: "#e6e0e9",
|
||||
surfaceVariant: "#49454e",
|
||||
surfaceVariantText: "#cac4cf",
|
||||
surfaceTint: "#D0BCFF",
|
||||
background: "#141218",
|
||||
backgroundText: "#e6e0e9",
|
||||
outline: "#948f99",
|
||||
surfaceContainer: "#211f24",
|
||||
surfaceContainerHigh: "#2b292f",
|
||||
surfaceContainerHighest: "#36343a",
|
||||
},
|
||||
green: {
|
||||
name: "Green",
|
||||
primary: "#4caf50",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#1b5e20",
|
||||
secondary: "#81c995",
|
||||
surface: "#10140f",
|
||||
surfaceText: "#e0e4db",
|
||||
surfaceVariant: "#424940",
|
||||
surfaceVariantText: "#c2c9bd",
|
||||
surfaceTint: "#81c995",
|
||||
background: "#10140f",
|
||||
backgroundText: "#e0e4db",
|
||||
outline: "#8c9388",
|
||||
surfaceContainer: "#1d211b",
|
||||
surfaceContainerHigh: "#272b25",
|
||||
surfaceContainerHighest: "#323630",
|
||||
},
|
||||
orange: {
|
||||
name: "Orange",
|
||||
primary: "#ff6d00",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#3e2723",
|
||||
secondary: "#ffb74d",
|
||||
surface: "#1a120e",
|
||||
surfaceText: "#f0dfd8",
|
||||
surfaceVariant: "#52443d",
|
||||
surfaceVariantText: "#d7c2b9",
|
||||
surfaceTint: "#ffb74d",
|
||||
background: "#1a120e",
|
||||
backgroundText: "#f0dfd8",
|
||||
outline: "#a08d85",
|
||||
surfaceContainer: "#271e1a",
|
||||
surfaceContainerHigh: "#322824",
|
||||
surfaceContainerHighest: "#3d332e",
|
||||
},
|
||||
red: {
|
||||
name: "Red",
|
||||
primary: "#f44336",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#4a0e0e",
|
||||
secondary: "#f28b82",
|
||||
surface: "#1a1110",
|
||||
surfaceText: "#f1dedc",
|
||||
surfaceVariant: "#534341",
|
||||
surfaceVariantText: "#d8c2be",
|
||||
surfaceTint: "#f28b82",
|
||||
background: "#1a1110",
|
||||
backgroundText: "#f1dedc",
|
||||
outline: "#a08c89",
|
||||
surfaceContainer: "#271d1c",
|
||||
surfaceContainerHigh: "#322826",
|
||||
surfaceContainerHighest: "#3d3231",
|
||||
},
|
||||
cyan: {
|
||||
name: "Cyan",
|
||||
primary: "#00bcd4",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#004d5c",
|
||||
secondary: "#4dd0e1",
|
||||
surface: "#0e1416",
|
||||
surfaceText: "#dee3e5",
|
||||
surfaceVariant: "#3f484a",
|
||||
surfaceVariantText: "#bfc8ca",
|
||||
surfaceTint: "#4dd0e1",
|
||||
background: "#0e1416",
|
||||
backgroundText: "#dee3e5",
|
||||
outline: "#899295",
|
||||
surfaceContainer: "#1b2122",
|
||||
surfaceContainerHigh: "#252b2c",
|
||||
surfaceContainerHighest: "#303637",
|
||||
},
|
||||
pink: {
|
||||
name: "Pink",
|
||||
primary: "#e91e63",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#4a0e2f",
|
||||
secondary: "#f8bbd9",
|
||||
surface: "#191112",
|
||||
surfaceText: "#f0dee0",
|
||||
surfaceVariant: "#524345",
|
||||
surfaceVariantText: "#d6c2c3",
|
||||
surfaceTint: "#f8bbd9",
|
||||
background: "#191112",
|
||||
backgroundText: "#f0dee0",
|
||||
outline: "#9f8c8e",
|
||||
surfaceContainer: "#261d1e",
|
||||
surfaceContainerHigh: "#312829",
|
||||
surfaceContainerHighest: "#3c3233",
|
||||
},
|
||||
amber: {
|
||||
name: "Amber",
|
||||
primary: "#ffc107",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#4a3c00",
|
||||
secondary: "#ffd54f",
|
||||
surface: "#17130b",
|
||||
surfaceText: "#ebe1d4",
|
||||
surfaceVariant: "#4d4639",
|
||||
surfaceVariantText: "#d0c5b4",
|
||||
surfaceTint: "#ffd54f",
|
||||
background: "#17130b",
|
||||
backgroundText: "#ebe1d4",
|
||||
outline: "#998f80",
|
||||
surfaceContainer: "#231f17",
|
||||
surfaceContainerHigh: "#2e2921",
|
||||
surfaceContainerHighest: "#39342b",
|
||||
},
|
||||
coral: {
|
||||
name: "Coral",
|
||||
primary: "#ffb4ab",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#8c1d18",
|
||||
secondary: "#f9dedc",
|
||||
surface: "#1a1110",
|
||||
surfaceText: "#f1dedc",
|
||||
surfaceVariant: "#534341",
|
||||
surfaceVariantText: "#d8c2bf",
|
||||
surfaceTint: "#ffb4ab",
|
||||
background: "#1a1110",
|
||||
backgroundText: "#f1dedc",
|
||||
outline: "#a08c8a",
|
||||
surfaceContainer: "#271d1c",
|
||||
surfaceContainerHigh: "#322826",
|
||||
surfaceContainerHighest: "#3d3231",
|
||||
},
|
||||
monochrome: {
|
||||
name: "Monochrome",
|
||||
primary: "#ffffff",
|
||||
primaryText: "#2b303c",
|
||||
primaryContainer: "#424753",
|
||||
secondary: "#c4c6d0",
|
||||
surface: "#2a2a2a",
|
||||
surfaceText: "#e4e2e3",
|
||||
surfaceVariant: "#474648",
|
||||
surfaceVariantText: "#c8c6c7",
|
||||
surfaceTint: "#c2c6d6",
|
||||
background: "#131315",
|
||||
backgroundText: "#e4e2e3",
|
||||
outline: "#929092",
|
||||
surfaceContainer: "#353535",
|
||||
surfaceContainerHigh: "#424242",
|
||||
surfaceContainerHighest: "#505050",
|
||||
error: "#ffb4ab",
|
||||
warning: "#3f4759",
|
||||
info: "#595e6c",
|
||||
matugen_type: "scheme-monochrome",
|
||||
},
|
||||
},
|
||||
LIGHT: {
|
||||
blue: {
|
||||
name: "Blue Light",
|
||||
primary: "#1976d2",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e3f2fd",
|
||||
secondary: "#42a5f5",
|
||||
surface: "#f7f9ff",
|
||||
surfaceText: "#181c20",
|
||||
surfaceVariant: "#dee3eb",
|
||||
surfaceVariantText: "#42474e",
|
||||
surfaceTint: "#1976d2",
|
||||
background: "#f7f9ff",
|
||||
backgroundText: "#181c20",
|
||||
outline: "#72777f",
|
||||
surfaceContainer: "#eceef4",
|
||||
surfaceContainerHigh: "#e6e8ee",
|
||||
surfaceContainerHighest: "#e0e2e8",
|
||||
},
|
||||
purple: {
|
||||
name: "Purple Light",
|
||||
primary: "#6750A4",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#EADDFF",
|
||||
secondary: "#625B71",
|
||||
surface: "#fef7ff",
|
||||
surfaceText: "#1d1b20",
|
||||
surfaceVariant: "#e7e0eb",
|
||||
surfaceVariantText: "#49454e",
|
||||
surfaceTint: "#6750A4",
|
||||
background: "#fef7ff",
|
||||
backgroundText: "#1d1b20",
|
||||
outline: "#7a757f",
|
||||
surfaceContainer: "#f2ecf4",
|
||||
surfaceContainerHigh: "#ece6ee",
|
||||
surfaceContainerHighest: "#e6e0e9",
|
||||
},
|
||||
green: {
|
||||
name: "Green Light",
|
||||
primary: "#2e7d32",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e8f5e8",
|
||||
secondary: "#4caf50",
|
||||
surface: "#f7fbf1",
|
||||
surfaceText: "#191d17",
|
||||
surfaceVariant: "#dee5d8",
|
||||
surfaceVariantText: "#424940",
|
||||
surfaceTint: "#2e7d32",
|
||||
background: "#f7fbf1",
|
||||
backgroundText: "#191d17",
|
||||
outline: "#72796f",
|
||||
surfaceContainer: "#ecefe6",
|
||||
surfaceContainerHigh: "#e6e9e0",
|
||||
surfaceContainerHighest: "#e0e4db",
|
||||
},
|
||||
orange: {
|
||||
name: "Orange Light",
|
||||
primary: "#e65100",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#ffecb3",
|
||||
secondary: "#ff9800",
|
||||
surface: "#fff8f6",
|
||||
surfaceText: "#221a16",
|
||||
surfaceVariant: "#f4ded5",
|
||||
surfaceVariantText: "#52443d",
|
||||
surfaceTint: "#e65100",
|
||||
background: "#fff8f6",
|
||||
backgroundText: "#221a16",
|
||||
outline: "#85736c",
|
||||
surfaceContainer: "#fceae3",
|
||||
surfaceContainerHigh: "#f6e5de",
|
||||
surfaceContainerHighest: "#f0dfd8",
|
||||
},
|
||||
red: {
|
||||
name: "Red Light",
|
||||
primary: "#d32f2f",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#ffebee",
|
||||
secondary: "#f44336",
|
||||
surface: "#fff8f7",
|
||||
surfaceText: "#231918",
|
||||
surfaceVariant: "#f5ddda",
|
||||
surfaceVariantText: "#534341",
|
||||
surfaceTint: "#d32f2f",
|
||||
background: "#fff8f7",
|
||||
backgroundText: "#231918",
|
||||
outline: "#857370",
|
||||
surfaceContainer: "#fceae7",
|
||||
surfaceContainerHigh: "#f7e4e1",
|
||||
surfaceContainerHighest: "#f1dedc",
|
||||
},
|
||||
cyan: {
|
||||
name: "Cyan Light",
|
||||
primary: "#0097a7",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e0f2f1",
|
||||
secondary: "#00bcd4",
|
||||
surface: "#f5fafc",
|
||||
surfaceText: "#171d1e",
|
||||
surfaceVariant: "#dbe4e6",
|
||||
surfaceVariantText: "#3f484a",
|
||||
surfaceTint: "#0097a7",
|
||||
background: "#f5fafc",
|
||||
backgroundText: "#171d1e",
|
||||
outline: "#6f797b",
|
||||
surfaceContainer: "#e9eff0",
|
||||
surfaceContainerHigh: "#e3e9eb",
|
||||
surfaceContainerHighest: "#dee3e5",
|
||||
},
|
||||
pink: {
|
||||
name: "Pink Light",
|
||||
primary: "#c2185b",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#fce4ec",
|
||||
secondary: "#e91e63",
|
||||
surface: "#fff8f7",
|
||||
surfaceText: "#22191a",
|
||||
surfaceVariant: "#f3dddf",
|
||||
surfaceVariantText: "#524345",
|
||||
surfaceTint: "#c2185b",
|
||||
background: "#fff8f7",
|
||||
backgroundText: "#22191a",
|
||||
outline: "#847375",
|
||||
surfaceContainer: "#fbeaeb",
|
||||
surfaceContainerHigh: "#f5e4e5",
|
||||
surfaceContainerHighest: "#f0dee0",
|
||||
},
|
||||
amber: {
|
||||
name: "Amber Light",
|
||||
primary: "#ff8f00",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#fff8e1",
|
||||
secondary: "#ffc107",
|
||||
surface: "#fff8f2",
|
||||
surfaceText: "#1f1b13",
|
||||
surfaceVariant: "#ede1cf",
|
||||
surfaceVariantText: "#4d4639",
|
||||
surfaceTint: "#ff8f00",
|
||||
background: "#fff8f2",
|
||||
backgroundText: "#1f1b13",
|
||||
outline: "#7f7667",
|
||||
surfaceContainer: "#f6ecdf",
|
||||
surfaceContainerHigh: "#f1e7d9",
|
||||
surfaceContainerHighest: "#ebe1d4",
|
||||
},
|
||||
coral: {
|
||||
name: "Coral Light",
|
||||
primary: "#8c1d18",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#ffdad6",
|
||||
secondary: "#ff5449",
|
||||
surface: "#fff8f7",
|
||||
surfaceText: "#231918",
|
||||
surfaceVariant: "#f5ddda",
|
||||
surfaceVariantText: "#534341",
|
||||
surfaceTint: "#8c1d18",
|
||||
background: "#fff8f7",
|
||||
backgroundText: "#231918",
|
||||
outline: "#857371",
|
||||
surfaceContainer: "#fceae7",
|
||||
surfaceContainerHigh: "#f6e4e2",
|
||||
surfaceContainerHighest: "#f1dedc",
|
||||
},
|
||||
monochrome: {
|
||||
name: "Monochrome Light",
|
||||
primary: "#2b303c",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#d6d7dc",
|
||||
secondary: "#4a4d56",
|
||||
surface: "#f5f5f6",
|
||||
surfaceText: "#2a2a2a",
|
||||
surfaceVariant: "#e0e0e2",
|
||||
surfaceVariantText: "#424242",
|
||||
surfaceTint: "#5a5f6e",
|
||||
background: "#ffffff",
|
||||
backgroundText: "#1a1a1a",
|
||||
outline: "#757577",
|
||||
surfaceContainer: "#e8e8ea",
|
||||
surfaceContainerHigh: "#dcdcde",
|
||||
surfaceContainerHighest: "#d0d0d2",
|
||||
error: "#ba1a1a",
|
||||
warning: "#f9e79f",
|
||||
info: "#5d6475",
|
||||
matugen_type: "scheme-monochrome",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const ThemeCategories = {
|
||||
GENERIC: {
|
||||
name: "Generic",
|
||||
variants: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"]
|
||||
},
|
||||
CATPPUCCIN: {
|
||||
name: "Catppuccin",
|
||||
variants: Object.keys(CatppuccinVariants)
|
||||
}
|
||||
}
|
||||
GENERIC: {
|
||||
name: "Generic",
|
||||
variants: [
|
||||
"blue",
|
||||
"purple",
|
||||
"green",
|
||||
"orange",
|
||||
"red",
|
||||
"cyan",
|
||||
"pink",
|
||||
"amber",
|
||||
"coral",
|
||||
"monochrome",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const ThemeNames = {
|
||||
BLUE: "blue",
|
||||
PURPLE: "purple",
|
||||
GREEN: "green",
|
||||
ORANGE: "orange",
|
||||
RED: "red",
|
||||
CYAN: "cyan",
|
||||
PINK: "pink",
|
||||
AMBER: "amber",
|
||||
CORAL: "coral",
|
||||
MONOCHROME: "monochrome",
|
||||
DYNAMIC: "dynamic"
|
||||
}
|
||||
BLUE: "blue",
|
||||
PURPLE: "purple",
|
||||
GREEN: "green",
|
||||
ORANGE: "orange",
|
||||
RED: "red",
|
||||
CYAN: "cyan",
|
||||
PINK: "pink",
|
||||
AMBER: "amber",
|
||||
CORAL: "coral",
|
||||
MONOCHROME: "monochrome",
|
||||
DYNAMIC: "dynamic",
|
||||
};
|
||||
|
||||
function isStockTheme(themeName) {
|
||||
return Object.keys(StockThemes.DARK).includes(themeName)
|
||||
}
|
||||
|
||||
function isCatppuccinVariant(themeName) {
|
||||
return Object.keys(CatppuccinVariants).includes(themeName)
|
||||
return Object.keys(StockThemes.DARK).includes(themeName);
|
||||
}
|
||||
|
||||
function getAvailableThemes(isLight = false) {
|
||||
return isLight ? StockThemes.LIGHT : StockThemes.DARK
|
||||
return isLight ? StockThemes.LIGHT : StockThemes.DARK;
|
||||
}
|
||||
|
||||
function getThemeByName(themeName, isLight = false) {
|
||||
if (isCatppuccinVariant(themeName)) {
|
||||
return getCatppuccinTheme(themeName, isLight)
|
||||
}
|
||||
const themes = getAvailableThemes(isLight)
|
||||
return themes[themeName] || themes.blue
|
||||
const themes = getAvailableThemes(isLight);
|
||||
return themes[themeName] || themes.blue;
|
||||
}
|
||||
|
||||
function getAllThemeNames() {
|
||||
return Object.keys(StockThemes.DARK)
|
||||
}
|
||||
|
||||
function getCatppuccinVariantNames() {
|
||||
return Object.keys(CatppuccinVariants)
|
||||
return Object.keys(StockThemes.DARK);
|
||||
}
|
||||
|
||||
function getThemeCategories() {
|
||||
return ThemeCategories
|
||||
return ThemeCategories;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,9 @@ Singleton {
|
||||
property var matugenColors: ({})
|
||||
property var _pendingGenerateParams: null
|
||||
property var customThemeData: null
|
||||
property var customThemeRawData: null
|
||||
readonly property var currentThemeVariants: customThemeRawData?.variants || null
|
||||
readonly property string currentThemeId: customThemeRawData?.id || ""
|
||||
|
||||
Component.onCompleted: {
|
||||
Quickshell.execDetached(["mkdir", "-p", stateDir]);
|
||||
@@ -257,7 +260,7 @@ Singleton {
|
||||
property color outlineVariant: currentThemeData.outlineVariant || Qt.rgba(outline.r, outline.g, outline.b, 0.6)
|
||||
property color surfaceContainer: currentThemeData.surfaceContainer
|
||||
property color surfaceContainerHigh: currentThemeData.surfaceContainerHigh
|
||||
property color surfaceContainerHighest: currentThemeData.surfaceContainerHighest
|
||||
property color surfaceContainerHighest: currentThemeData.surfaceContainerHighest || surfaceContainerHigh
|
||||
|
||||
property color onSurface: surfaceText
|
||||
property color onSurfaceVariant: surfaceVariantText
|
||||
@@ -474,24 +477,28 @@ Singleton {
|
||||
|
||||
if (themeName === dynamic) {
|
||||
currentTheme = dynamic;
|
||||
currentThemeCategory = dynamic;
|
||||
if (currentThemeCategory !== "registry")
|
||||
currentThemeCategory = dynamic;
|
||||
} else if (themeName === custom) {
|
||||
currentTheme = custom;
|
||||
currentThemeCategory = custom;
|
||||
if (currentThemeCategory !== "registry")
|
||||
currentThemeCategory = custom;
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.customThemeFile) {
|
||||
loadCustomThemeFromFile(SettingsData.customThemeFile);
|
||||
}
|
||||
} else if (themeName === "" && currentThemeCategory === "registry") {
|
||||
// Registry category selected but no theme chosen yet
|
||||
} else {
|
||||
currentTheme = themeName;
|
||||
if (StockThemes.isCatppuccinVariant(themeName)) {
|
||||
currentThemeCategory = "catppuccin";
|
||||
} else {
|
||||
if (currentThemeCategory !== "registry") {
|
||||
currentThemeCategory = "generic";
|
||||
}
|
||||
}
|
||||
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode);
|
||||
if (savePrefs && typeof SettingsData !== "undefined" && !isGreeterMode)
|
||||
if (savePrefs && typeof SettingsData !== "undefined" && !isGreeterMode) {
|
||||
SettingsData.set("currentThemeCategory", currentThemeCategory);
|
||||
SettingsData.set("currentThemeName", currentTheme);
|
||||
}
|
||||
|
||||
if (!isGreeterMode) {
|
||||
generateSystemThemesFromCurrentTheme();
|
||||
@@ -553,62 +560,103 @@ Singleton {
|
||||
themeCategoryTransitionTimer.restart();
|
||||
}
|
||||
|
||||
function getCatppuccinColor(variantName) {
|
||||
const catColors = {
|
||||
"cat-rosewater": "#f5e0dc",
|
||||
"cat-flamingo": "#f2cdcd",
|
||||
"cat-pink": "#f5c2e7",
|
||||
"cat-mauve": "#cba6f7",
|
||||
"cat-red": "#f38ba8",
|
||||
"cat-maroon": "#eba0ac",
|
||||
"cat-peach": "#fab387",
|
||||
"cat-yellow": "#f9e2af",
|
||||
"cat-green": "#a6e3a1",
|
||||
"cat-teal": "#94e2d5",
|
||||
"cat-sky": "#89dceb",
|
||||
"cat-sapphire": "#74c7ec",
|
||||
"cat-blue": "#89b4fa",
|
||||
"cat-lavender": "#b4befe"
|
||||
};
|
||||
return catColors[variantName] || "#cba6f7";
|
||||
}
|
||||
|
||||
function getCatppuccinVariantName(variantName) {
|
||||
const catNames = {
|
||||
"cat-rosewater": "Rosewater",
|
||||
"cat-flamingo": "Flamingo",
|
||||
"cat-pink": "Pink",
|
||||
"cat-mauve": "Mauve",
|
||||
"cat-red": "Red",
|
||||
"cat-maroon": "Maroon",
|
||||
"cat-peach": "Peach",
|
||||
"cat-yellow": "Yellow",
|
||||
"cat-green": "Green",
|
||||
"cat-teal": "Teal",
|
||||
"cat-sky": "Sky",
|
||||
"cat-sapphire": "Sapphire",
|
||||
"cat-blue": "Blue",
|
||||
"cat-lavender": "Lavender"
|
||||
};
|
||||
return catNames[variantName] || "Unknown";
|
||||
}
|
||||
|
||||
function loadCustomTheme(themeData) {
|
||||
customThemeRawData = themeData;
|
||||
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark";
|
||||
|
||||
var baseColors = {};
|
||||
if (themeData.dark || themeData.light) {
|
||||
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark";
|
||||
const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light;
|
||||
customThemeData = selectedTheme;
|
||||
baseColors = themeData[colorMode] || themeData.dark || themeData.light || {};
|
||||
} else {
|
||||
customThemeData = themeData;
|
||||
baseColors = themeData;
|
||||
}
|
||||
|
||||
if (themeData.variants) {
|
||||
const themeId = themeData.id || "";
|
||||
|
||||
if (themeData.variants.type === "multi" && themeData.variants.flavors && themeData.variants.accents) {
|
||||
const defaults = themeData.variants.defaults || {};
|
||||
const modeDefaults = defaults[colorMode] || defaults.dark || {};
|
||||
const stored = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, modeDefaults) : modeDefaults;
|
||||
var flavorId = stored.flavor || modeDefaults.flavor || "";
|
||||
const accentId = stored.accent || modeDefaults.accent || "";
|
||||
var flavor = findVariant(themeData.variants.flavors, flavorId);
|
||||
if (flavor) {
|
||||
const hasCurrentModeColors = flavor[colorMode] && (flavor[colorMode].primary || flavor[colorMode].surface);
|
||||
if (!hasCurrentModeColors) {
|
||||
flavorId = modeDefaults.flavor || "";
|
||||
flavor = findVariant(themeData.variants.flavors, flavorId);
|
||||
}
|
||||
}
|
||||
const accent = findAccent(themeData.variants.accents, accentId);
|
||||
if (flavor) {
|
||||
const flavorColors = flavor[colorMode] || flavor.dark || flavor.light || {};
|
||||
baseColors = mergeColors(baseColors, flavorColors);
|
||||
}
|
||||
if (accent && flavor) {
|
||||
const accentColors = accent[flavor.id] || {};
|
||||
baseColors = mergeColors(baseColors, accentColors);
|
||||
}
|
||||
customThemeData = baseColors;
|
||||
generateSystemThemesFromCurrentTheme();
|
||||
return;
|
||||
}
|
||||
|
||||
if (themeData.variants.options && themeData.variants.options.length > 0) {
|
||||
const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, themeData.variants.default) : themeData.variants.default;
|
||||
const variant = findVariant(themeData.variants.options, selectedVariantId);
|
||||
if (variant) {
|
||||
const variantColors = variant[colorMode] || variant.dark || variant.light || {};
|
||||
customThemeData = mergeColors(baseColors, variantColors);
|
||||
generateSystemThemesFromCurrentTheme();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customThemeData = baseColors;
|
||||
generateSystemThemesFromCurrentTheme();
|
||||
}
|
||||
|
||||
function findVariant(options, variantId) {
|
||||
if (!variantId || !options)
|
||||
return null;
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
if (options[i].id === variantId)
|
||||
return options[i];
|
||||
}
|
||||
return options[0] || null;
|
||||
}
|
||||
|
||||
function findAccent(accents, accentId) {
|
||||
if (!accentId || !accents)
|
||||
return null;
|
||||
for (var i = 0; i < accents.length; i++) {
|
||||
if (accents[i].id === accentId)
|
||||
return accents[i];
|
||||
}
|
||||
return accents[0] || null;
|
||||
}
|
||||
|
||||
function mergeColors(base, overlay) {
|
||||
var result = JSON.parse(JSON.stringify(base));
|
||||
for (var key in overlay) {
|
||||
if (overlay[key])
|
||||
result[key] = overlay[key];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function loadCustomThemeFromFile(filePath) {
|
||||
customThemeFileView.path = filePath;
|
||||
}
|
||||
|
||||
function reloadCustomThemeVariant() {
|
||||
if (currentTheme !== "custom" || !customThemeRawData)
|
||||
return;
|
||||
loadCustomTheme(customThemeRawData);
|
||||
}
|
||||
|
||||
property alias availableThemeNames: root._availableThemeNames
|
||||
readonly property var _availableThemeNames: StockThemes.getAllThemeNames()
|
||||
property string currentThemeName: currentTheme
|
||||
@@ -639,10 +687,9 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
property var widgetBaseHoverColor: {
|
||||
const baseColor = widgetBaseBackgroundColor;
|
||||
const factor = 1.2;
|
||||
return isLightMode ? Qt.darker(baseColor, factor) : Qt.lighter(baseColor, factor);
|
||||
property color widgetBaseHoverColor: {
|
||||
const blended = blend(widgetBaseBackgroundColor, primary, 0.1);
|
||||
return withAlpha(blended, Math.max(0.3, blended.a));
|
||||
}
|
||||
|
||||
property color widgetIconColor: {
|
||||
@@ -829,7 +876,7 @@ Singleton {
|
||||
if (typeof SettingsData !== "undefined") {
|
||||
const skipTemplates = [];
|
||||
if (!SettingsData.runDmsMatugenTemplates) {
|
||||
skipTemplates.push("gtk", "niri", "qt5ct", "qt6ct", "firefox", "pywalfox", "vesktop", "ghostty", "kitty", "foot", "alacritty", "wezterm", "dgop", "kcolorscheme", "vscode");
|
||||
skipTemplates.push("gtk", "neovim", "niri", "qt5ct", "qt6ct", "firefox", "pywalfox", "vesktop", "equibop", "ghostty", "kitty", "foot", "alacritty", "wezterm", "dgop", "kcolorscheme", "vscode");
|
||||
} else {
|
||||
if (!SettingsData.matugenTemplateGtk)
|
||||
skipTemplates.push("gtk");
|
||||
@@ -845,12 +892,16 @@ Singleton {
|
||||
skipTemplates.push("pywalfox");
|
||||
if (!SettingsData.matugenTemplateVesktop)
|
||||
skipTemplates.push("vesktop");
|
||||
if (!SettingsData.matugenTemplateEquibop)
|
||||
skipTemplates.push("equibop");
|
||||
if (!SettingsData.matugenTemplateGhostty)
|
||||
skipTemplates.push("ghostty");
|
||||
if (!SettingsData.matugenTemplateKitty)
|
||||
skipTemplates.push("kitty");
|
||||
if (!SettingsData.matugenTemplateFoot)
|
||||
skipTemplates.push("foot");
|
||||
if (!SettingsData.matugenTemplateNeovim)
|
||||
skipTemplates.push("nvim");
|
||||
if (!SettingsData.matugenTemplateAlacritty)
|
||||
skipTemplates.push("alacritty");
|
||||
if (!SettingsData.matugenTemplateWezterm)
|
||||
@@ -899,8 +950,48 @@ Singleton {
|
||||
|
||||
let darkTheme, lightTheme;
|
||||
if (currentTheme === "custom") {
|
||||
darkTheme = customThemeData;
|
||||
lightTheme = customThemeData;
|
||||
if (customThemeRawData && (customThemeRawData.dark || customThemeRawData.light)) {
|
||||
darkTheme = customThemeRawData.dark || customThemeRawData.light;
|
||||
lightTheme = customThemeRawData.light || customThemeRawData.dark;
|
||||
|
||||
if (customThemeRawData.variants) {
|
||||
const themeId = customThemeRawData.id || "";
|
||||
|
||||
if (customThemeRawData.variants.type === "multi" && customThemeRawData.variants.flavors && customThemeRawData.variants.accents) {
|
||||
const defaults = customThemeRawData.variants.defaults || {};
|
||||
const darkDefaults = defaults.dark || {};
|
||||
const lightDefaults = defaults.light || defaults.dark || {};
|
||||
const storedDark = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, darkDefaults) : darkDefaults;
|
||||
const storedLight = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, lightDefaults) : lightDefaults;
|
||||
const darkFlavorId = storedDark.flavor || darkDefaults.flavor || "";
|
||||
const lightFlavorId = storedLight.flavor || lightDefaults.flavor || "";
|
||||
const accentId = storedDark.accent || darkDefaults.accent || "";
|
||||
const darkFlavor = findVariant(customThemeRawData.variants.flavors, darkFlavorId);
|
||||
const lightFlavor = findVariant(customThemeRawData.variants.flavors, lightFlavorId);
|
||||
const accent = findAccent(customThemeRawData.variants.accents, accentId);
|
||||
if (darkFlavor) {
|
||||
darkTheme = mergeColors(darkTheme, darkFlavor.dark || {});
|
||||
if (accent)
|
||||
darkTheme = mergeColors(darkTheme, accent[darkFlavor.id] || {});
|
||||
}
|
||||
if (lightFlavor) {
|
||||
lightTheme = mergeColors(lightTheme, lightFlavor.light || {});
|
||||
if (accent)
|
||||
lightTheme = mergeColors(lightTheme, accent[lightFlavor.id] || {});
|
||||
}
|
||||
} else if (customThemeRawData.variants.options) {
|
||||
const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, customThemeRawData.variants.default) : customThemeRawData.variants.default;
|
||||
const variant = findVariant(customThemeRawData.variants.options, selectedVariantId);
|
||||
if (variant) {
|
||||
darkTheme = mergeColors(darkTheme, variant.dark || {});
|
||||
lightTheme = mergeColors(lightTheme, variant.light || {});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
darkTheme = customThemeData;
|
||||
lightTheme = customThemeData;
|
||||
}
|
||||
} else {
|
||||
darkTheme = StockThemes.getThemeByName(currentTheme, false);
|
||||
lightTheme = StockThemes.getThemeByName(currentTheme, true);
|
||||
@@ -918,6 +1009,7 @@ Singleton {
|
||||
|
||||
function buildMatugenColorsFromTheme(darkTheme, lightTheme) {
|
||||
const colors = {};
|
||||
const isLight = SessionData !== "undefined" && SessionData.isLightMode;
|
||||
|
||||
function addColor(matugenKey, darkVal, lightVal) {
|
||||
if (!darkVal && !lightVal)
|
||||
@@ -930,7 +1022,7 @@ Singleton {
|
||||
"color": String(lightVal || darkVal)
|
||||
},
|
||||
"default": {
|
||||
"color": String(darkVal || lightVal)
|
||||
"color": String((isLight && lightVal) ? lightVal : darkVal)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,9 +21,12 @@ Singleton {
|
||||
showNetworkIcon: true,
|
||||
showBluetoothIcon: true,
|
||||
showAudioIcon: true,
|
||||
showAudioPercent: true,
|
||||
showVpnIcon: true,
|
||||
showBrightnessIcon: false,
|
||||
showBrightnessPercent: false,
|
||||
showMicIcon: false,
|
||||
showMicPercent: true,
|
||||
showBatteryIcon: false,
|
||||
showPrinterIcon: false
|
||||
};
|
||||
@@ -65,12 +68,18 @@ Singleton {
|
||||
item.showBluetoothIcon = order[i].showBluetoothIcon;
|
||||
if (isObj && order[i].showAudioIcon !== undefined)
|
||||
item.showAudioIcon = order[i].showAudioIcon;
|
||||
if (isObj && order[i].showAudioPercent !== undefined)
|
||||
item.showAudioPercent = order[i].showAudioPercent;
|
||||
if (isObj && order[i].showVpnIcon !== undefined)
|
||||
item.showVpnIcon = order[i].showVpnIcon;
|
||||
if (isObj && order[i].showBrightnessIcon !== undefined)
|
||||
item.showBrightnessIcon = order[i].showBrightnessIcon;
|
||||
if (isObj && order[i].showBrightnessPercent !== undefined)
|
||||
item.showBrightnessPercent = order[i].showBrightnessPercent;
|
||||
if (isObj && order[i].showMicIcon !== undefined)
|
||||
item.showMicIcon = order[i].showMicIcon;
|
||||
if (isObj && order[i].showMicPercent !== undefined)
|
||||
item.showMicPercent = order[i].showMicPercent;
|
||||
if (isObj && order[i].showBatteryIcon !== undefined)
|
||||
item.showBatteryIcon = order[i].showBatteryIcon;
|
||||
if (isObj && order[i].showPrinterIcon !== undefined)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
@@ -11,61 +10,20 @@ Singleton {
|
||||
|
||||
property var settingsRoot: null
|
||||
|
||||
function detectIcons() {
|
||||
systemDefaultDetectionProcess.running = true
|
||||
}
|
||||
|
||||
function detectQtTools() {
|
||||
qtToolsDetectionProcess.running = true
|
||||
qtToolsDetectionProcess.running = true;
|
||||
}
|
||||
|
||||
function detectFprintd() {
|
||||
fprintdDetectionProcess.running = true
|
||||
fprintdDetectionProcess.running = true;
|
||||
}
|
||||
|
||||
function checkPluginSettings() {
|
||||
pluginSettingsCheckProcess.running = true
|
||||
pluginSettingsCheckProcess.running = true;
|
||||
}
|
||||
|
||||
function checkDefaultSettings() {
|
||||
defaultSettingsCheckProcess.running = true
|
||||
}
|
||||
|
||||
property var systemDefaultDetectionProcess: Process {
|
||||
command: ["sh", "-c", "gsettings get org.gnome.desktop.interface icon-theme 2>/dev/null | sed \"s/'//g\" || echo ''"]
|
||||
running: false
|
||||
onExited: function(exitCode) {
|
||||
if (!settingsRoot) return;
|
||||
if (exitCode === 0 && stdout && stdout.length > 0) {
|
||||
settingsRoot.systemDefaultIconTheme = stdout.trim();
|
||||
} else {
|
||||
settingsRoot.systemDefaultIconTheme = "";
|
||||
}
|
||||
iconThemeDetectionProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
property var iconThemeDetectionProcess: Process {
|
||||
|
||||
command: ["sh", "-c", "find /usr/share/icons ~/.local/share/icons ~/.icons -maxdepth 1 -type d 2>/dev/null | sed 's|.*/||' | grep -v '^icons$' | sort -u"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (!settingsRoot) return
|
||||
var detectedThemes = ["System Default"]
|
||||
if (text && text.trim()) {
|
||||
var themes = text.trim().split('\n')
|
||||
for (var i = 0; i < themes.length; i++) {
|
||||
var theme = themes[i].trim()
|
||||
if (theme && theme !== "" && theme !== "default" && theme !== "hicolor" && theme !== "locolor") {
|
||||
detectedThemes.push(theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
settingsRoot.availableIconThemes = detectedThemes
|
||||
}
|
||||
}
|
||||
defaultSettingsCheckProcess.running = true;
|
||||
}
|
||||
|
||||
property var qtToolsDetectionProcess: Process {
|
||||
@@ -74,7 +32,8 @@ Singleton {
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (!settingsRoot) return;
|
||||
if (!settingsRoot)
|
||||
return;
|
||||
if (text && text.trim()) {
|
||||
var lines = text.trim().split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
@@ -95,8 +54,9 @@ Singleton {
|
||||
property var defaultSettingsCheckProcess: Process {
|
||||
command: ["sh", "-c", "CONFIG_DIR=\"" + (settingsRoot?._configDir || "") + "/DankMaterialShell\"; if [ -f \"$CONFIG_DIR/default-settings.json\" ] && [ ! -f \"$CONFIG_DIR/settings.json\" ]; then cp --no-preserve=mode \"$CONFIG_DIR/default-settings.json\" \"$CONFIG_DIR/settings.json\" && echo 'copied'; else echo 'not_found'; fi"]
|
||||
running: false
|
||||
onExited: function(exitCode) {
|
||||
if (!settingsRoot) return;
|
||||
onExited: function (exitCode) {
|
||||
if (!settingsRoot)
|
||||
return;
|
||||
if (exitCode === 0) {
|
||||
console.info("Copied default-settings.json to settings.json");
|
||||
if (settingsRoot.settingsFile) {
|
||||
@@ -113,8 +73,9 @@ Singleton {
|
||||
property var fprintdDetectionProcess: Process {
|
||||
command: ["sh", "-c", "command -v fprintd-list >/dev/null 2>&1"]
|
||||
running: false
|
||||
onExited: function(exitCode) {
|
||||
if (!settingsRoot) return;
|
||||
onExited: function (exitCode) {
|
||||
if (!settingsRoot)
|
||||
return;
|
||||
settingsRoot.fprintdAvailable = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
@@ -123,8 +84,9 @@ Singleton {
|
||||
command: ["test", "-f", settingsRoot?.pluginSettingsPath || ""]
|
||||
running: false
|
||||
|
||||
onExited: function(exitCode) {
|
||||
if (!settingsRoot) return;
|
||||
onExited: function (exitCode) {
|
||||
if (!settingsRoot)
|
||||
return;
|
||||
settingsRoot.pluginSettingsFileExists = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ function percentToUnit(v) {
|
||||
|
||||
var SPEC = {
|
||||
currentThemeName: { def: "blue", onChange: "applyStoredTheme" },
|
||||
currentThemeCategory: { def: "generic" },
|
||||
customThemeFile: { def: "" },
|
||||
registryThemeVariants: { def: {} },
|
||||
matugenScheme: { def: "scheme-tonal-spot", onChange: "regenSystemThemes" },
|
||||
runUserMatugenTemplates: { def: true, onChange: "regenSystemThemes" },
|
||||
matugenTargetMonitor: { def: "", onChange: "regenSystemThemes" },
|
||||
@@ -18,6 +20,8 @@ var SPEC = {
|
||||
widgetBackgroundColor: { def: "sch" },
|
||||
widgetColorMode: { def: "default" },
|
||||
cornerRadius: { def: 12, onChange: "updateNiriLayout" },
|
||||
niriLayoutGapsOverride: { def: -1, onChange: "updateNiriLayout" },
|
||||
niriLayoutRadiusOverride: { def: -1, onChange: "updateNiriLayout" },
|
||||
|
||||
use24HourClock: { def: true },
|
||||
showSeconds: { def: false },
|
||||
@@ -51,9 +55,12 @@ var SPEC = {
|
||||
controlCenterShowNetworkIcon: { def: true },
|
||||
controlCenterShowBluetoothIcon: { def: true },
|
||||
controlCenterShowAudioIcon: { def: true },
|
||||
controlCenterShowAudioPercent: { def: false },
|
||||
controlCenterShowVpnIcon: { def: true },
|
||||
controlCenterShowBrightnessIcon: { def: false },
|
||||
controlCenterShowBrightnessPercent: { def: false },
|
||||
controlCenterShowMicIcon: { def: false },
|
||||
controlCenterShowMicPercent: { def: false },
|
||||
controlCenterShowBatteryIcon: { def: false },
|
||||
controlCenterShowPrinterIcon: { def: false },
|
||||
|
||||
@@ -80,11 +87,13 @@ var SPEC = {
|
||||
maxWorkspaceIcons: { def: 3 },
|
||||
workspacesPerMonitor: { def: true },
|
||||
showOccupiedWorkspacesOnly: { def: false },
|
||||
reverseScrolling: { def: false },
|
||||
dwlShowAllTags: { def: false },
|
||||
workspaceNameIcons: { def: {} },
|
||||
waveProgressEnabled: { def: true },
|
||||
scrollTitleEnabled: { def: true },
|
||||
audioVisualizerEnabled: { def: true },
|
||||
audioScrollEnabled: { def: true },
|
||||
clockCompactMode: { def: false },
|
||||
focusedWindowCompactMode: { def: false },
|
||||
runningAppsCompactMode: { def: true },
|
||||
@@ -154,6 +163,7 @@ var SPEC = {
|
||||
batterySuspendTimeout: { def: 0 },
|
||||
batterySuspendBehavior: { def: 0 },
|
||||
batteryProfileName: { def: "" },
|
||||
batteryChargeLimit: { def: 100 },
|
||||
lockBeforeSuspend: { def: false },
|
||||
loginctlLockIntegration: { def: true },
|
||||
fadeToLockEnabled: { def: false },
|
||||
@@ -178,10 +188,12 @@ var SPEC = {
|
||||
matugenTemplateFirefox: { def: true },
|
||||
matugenTemplatePywalfox: { def: true },
|
||||
matugenTemplateVesktop: { def: true },
|
||||
matugenTemplateEquibop: { def: true },
|
||||
matugenTemplateGhostty: { def: true },
|
||||
matugenTemplateKitty: { def: true },
|
||||
matugenTemplateFoot: { def: true },
|
||||
matugenTemplateAlacritty: { def: true },
|
||||
matugenTemplateNeovim: { def: true },
|
||||
matugenTemplateWezterm: { def: true },
|
||||
matugenTemplateDgop: { def: true },
|
||||
matugenTemplateKcolorscheme: { def: true },
|
||||
@@ -201,6 +213,7 @@ var SPEC = {
|
||||
dockBorderColor: { def: "surfaceText" },
|
||||
dockBorderOpacity: { def: 1.0, coerce: percentToUnit },
|
||||
dockBorderThickness: { def: 1 },
|
||||
dockIsolateDisplays: { def: false },
|
||||
|
||||
notificationOverlayEnabled: { def: false },
|
||||
overviewRows: { def: 2, persist: false },
|
||||
@@ -258,6 +271,8 @@ var SPEC = {
|
||||
displayNameMode: { def: "system" },
|
||||
screenPreferences: { def: {} },
|
||||
showOnLastDisplay: { def: {} },
|
||||
niriOutputSettings: { def: {} },
|
||||
hyprlandOutputSettings: { def: {} },
|
||||
|
||||
barConfigs: { def: [{
|
||||
id: "default",
|
||||
@@ -290,6 +305,7 @@ var SPEC = {
|
||||
fontScale: 1.0,
|
||||
autoHide: false,
|
||||
autoHideDelay: 250,
|
||||
showOnWindowsOpen: false,
|
||||
openOnOverview: false,
|
||||
visible: true,
|
||||
popupGapsAuto: true,
|
||||
@@ -298,7 +314,52 @@ var SPEC = {
|
||||
scrollEnabled: true,
|
||||
scrollXBehavior: "column",
|
||||
scrollYBehavior: "workspace"
|
||||
}], onChange: "updateBarConfigs" }
|
||||
}], onChange: "updateBarConfigs" },
|
||||
|
||||
desktopClockEnabled: { def: false },
|
||||
desktopClockStyle: { def: "analog" },
|
||||
desktopClockTransparency: { def: 0.8, coerce: percentToUnit },
|
||||
desktopClockColorMode: { def: "primary" },
|
||||
desktopClockCustomColor: { def: "#ffffff" },
|
||||
desktopClockShowDate: { def: true },
|
||||
desktopClockShowAnalogNumbers: { def: false },
|
||||
desktopClockShowAnalogSeconds: { def: true },
|
||||
desktopClockX: { def: -1 },
|
||||
desktopClockY: { def: -1 },
|
||||
desktopClockWidth: { def: 280 },
|
||||
desktopClockHeight: { def: 180 },
|
||||
desktopClockDisplayPreferences: { def: ["all"] },
|
||||
|
||||
systemMonitorEnabled: { def: false },
|
||||
systemMonitorShowHeader: { def: true },
|
||||
systemMonitorTransparency: { def: 0.8, coerce: percentToUnit },
|
||||
systemMonitorColorMode: { def: "primary" },
|
||||
systemMonitorCustomColor: { def: "#ffffff" },
|
||||
systemMonitorShowCpu: { def: true },
|
||||
systemMonitorShowCpuGraph: { def: true },
|
||||
systemMonitorShowCpuTemp: { def: true },
|
||||
systemMonitorShowGpuTemp: { def: false },
|
||||
systemMonitorGpuPciId: { def: "" },
|
||||
systemMonitorShowMemory: { def: true },
|
||||
systemMonitorShowMemoryGraph: { def: true },
|
||||
systemMonitorShowNetwork: { def: true },
|
||||
systemMonitorShowNetworkGraph: { def: true },
|
||||
systemMonitorShowDisk: { def: true },
|
||||
systemMonitorShowTopProcesses: { def: false },
|
||||
systemMonitorTopProcessCount: { def: 3 },
|
||||
systemMonitorTopProcessSortBy: { def: "cpu" },
|
||||
systemMonitorGraphInterval: { def: 60 },
|
||||
systemMonitorLayoutMode: { def: "auto" },
|
||||
systemMonitorX: { def: -1 },
|
||||
systemMonitorY: { def: -1 },
|
||||
systemMonitorWidth: { def: 320 },
|
||||
systemMonitorHeight: { def: 480 },
|
||||
systemMonitorDisplayPreferences: { def: ["all"] },
|
||||
systemMonitorVariants: { def: [] },
|
||||
desktopWidgetPositions: { def: {} },
|
||||
desktopWidgetGridSettings: { def: {} },
|
||||
|
||||
desktopWidgetInstances: { def: [] }
|
||||
};
|
||||
|
||||
function getValidKeys() {
|
||||
|
||||
@@ -119,6 +119,101 @@ function migrateToVersion(obj, targetVersion) {
|
||||
settings.configVersion = 3;
|
||||
}
|
||||
|
||||
if (currentVersion < 4) {
|
||||
console.info("Migrating settings from version", currentVersion, "to version 4");
|
||||
console.info("Migrating desktop widgets to unified desktopWidgetInstances");
|
||||
|
||||
var instances = [];
|
||||
|
||||
if (settings.desktopClockEnabled) {
|
||||
var clockPositions = {};
|
||||
if (settings.desktopClockX !== undefined && settings.desktopClockX >= 0) {
|
||||
clockPositions["default"] = {
|
||||
x: settings.desktopClockX,
|
||||
y: settings.desktopClockY,
|
||||
width: settings.desktopClockWidth || 280,
|
||||
height: settings.desktopClockHeight || 180
|
||||
};
|
||||
}
|
||||
|
||||
instances.push({
|
||||
id: "dw_clock_primary",
|
||||
widgetType: "desktopClock",
|
||||
name: "Desktop Clock",
|
||||
enabled: true,
|
||||
config: {
|
||||
style: settings.desktopClockStyle || "analog",
|
||||
transparency: settings.desktopClockTransparency !== undefined ? settings.desktopClockTransparency : 0.8,
|
||||
colorMode: settings.desktopClockColorMode || "primary",
|
||||
customColor: settings.desktopClockCustomColor || "#ffffff",
|
||||
showDate: settings.desktopClockShowDate !== false,
|
||||
showAnalogNumbers: settings.desktopClockShowAnalogNumbers || false,
|
||||
showAnalogSeconds: settings.desktopClockShowAnalogSeconds !== false,
|
||||
displayPreferences: settings.desktopClockDisplayPreferences || ["all"]
|
||||
},
|
||||
positions: clockPositions
|
||||
});
|
||||
}
|
||||
|
||||
if (settings.systemMonitorEnabled) {
|
||||
var sysmonPositions = {};
|
||||
if (settings.systemMonitorX !== undefined && settings.systemMonitorX >= 0) {
|
||||
sysmonPositions["default"] = {
|
||||
x: settings.systemMonitorX,
|
||||
y: settings.systemMonitorY,
|
||||
width: settings.systemMonitorWidth || 320,
|
||||
height: settings.systemMonitorHeight || 480
|
||||
};
|
||||
}
|
||||
|
||||
instances.push({
|
||||
id: "dw_sysmon_primary",
|
||||
widgetType: "systemMonitor",
|
||||
name: "System Monitor",
|
||||
enabled: true,
|
||||
config: {
|
||||
showHeader: settings.systemMonitorShowHeader !== false,
|
||||
transparency: settings.systemMonitorTransparency !== undefined ? settings.systemMonitorTransparency : 0.8,
|
||||
colorMode: settings.systemMonitorColorMode || "primary",
|
||||
customColor: settings.systemMonitorCustomColor || "#ffffff",
|
||||
showCpu: settings.systemMonitorShowCpu !== false,
|
||||
showCpuGraph: settings.systemMonitorShowCpuGraph !== false,
|
||||
showCpuTemp: settings.systemMonitorShowCpuTemp !== false,
|
||||
showGpuTemp: settings.systemMonitorShowGpuTemp || false,
|
||||
gpuPciId: settings.systemMonitorGpuPciId || "",
|
||||
showMemory: settings.systemMonitorShowMemory !== false,
|
||||
showMemoryGraph: settings.systemMonitorShowMemoryGraph !== false,
|
||||
showNetwork: settings.systemMonitorShowNetwork !== false,
|
||||
showNetworkGraph: settings.systemMonitorShowNetworkGraph !== false,
|
||||
showDisk: settings.systemMonitorShowDisk !== false,
|
||||
showTopProcesses: settings.systemMonitorShowTopProcesses || false,
|
||||
topProcessCount: settings.systemMonitorTopProcessCount || 3,
|
||||
topProcessSortBy: settings.systemMonitorTopProcessSortBy || "cpu",
|
||||
layoutMode: settings.systemMonitorLayoutMode || "auto",
|
||||
graphInterval: settings.systemMonitorGraphInterval || 60,
|
||||
displayPreferences: settings.systemMonitorDisplayPreferences || ["all"]
|
||||
},
|
||||
positions: sysmonPositions
|
||||
});
|
||||
}
|
||||
|
||||
var variants = settings.systemMonitorVariants || [];
|
||||
for (var i = 0; i < variants.length; i++) {
|
||||
var v = variants[i];
|
||||
instances.push({
|
||||
id: v.id,
|
||||
widgetType: "systemMonitor",
|
||||
name: v.name || ("System Monitor " + (i + 2)),
|
||||
enabled: true,
|
||||
config: v.config || {},
|
||||
positions: v.positions || {}
|
||||
});
|
||||
}
|
||||
|
||||
settings.desktopWidgetInstances = instances;
|
||||
settings.configVersion = 4;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ Item {
|
||||
|
||||
WallpaperBackground {}
|
||||
|
||||
DesktopWidgetLayer {}
|
||||
|
||||
Lock {
|
||||
id: lock
|
||||
}
|
||||
@@ -508,6 +510,22 @@ Item {
|
||||
Connections {
|
||||
target: DMSService
|
||||
function onOpenUrlRequested(url) {
|
||||
if (url.startsWith("dms://theme/install/")) {
|
||||
var themeId = url.replace("dms://theme/install/", "").split(/[?#]/)[0];
|
||||
if (themeId) {
|
||||
PopoutService.pendingThemeInstall = themeId;
|
||||
PopoutService.openSettingsWithTab("theme");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (url.startsWith("dms://plugin/install/")) {
|
||||
var pluginId = url.replace("dms://plugin/install/", "").split(/[?#]/)[0];
|
||||
if (pluginId) {
|
||||
PopoutService.pendingPluginInstall = pluginId;
|
||||
PopoutService.openSettingsWithTab("plugins");
|
||||
}
|
||||
return;
|
||||
}
|
||||
browserPickerModal.url = url;
|
||||
browserPickerModal.open();
|
||||
}
|
||||
|
||||
@@ -893,4 +893,58 @@ Item {
|
||||
|
||||
target: "clipboard"
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function toggleOverlay(instanceId: string): string {
|
||||
if (!instanceId)
|
||||
return "ERROR: No instance ID specified";
|
||||
|
||||
const instance = SettingsData.getDesktopWidgetInstance(instanceId);
|
||||
if (!instance)
|
||||
return `DESKTOP_WIDGET_NOT_FOUND: ${instanceId}`;
|
||||
|
||||
const currentValue = instance.config?.showOnOverlay ?? false;
|
||||
SettingsData.updateDesktopWidgetInstanceConfig(instanceId, {
|
||||
showOnOverlay: !currentValue
|
||||
});
|
||||
return !currentValue ? `DESKTOP_WIDGET_OVERLAY_ENABLED: ${instanceId}` : `DESKTOP_WIDGET_OVERLAY_DISABLED: ${instanceId}`;
|
||||
}
|
||||
|
||||
function setOverlay(instanceId: string, enabled: string): string {
|
||||
if (!instanceId)
|
||||
return "ERROR: No instance ID specified";
|
||||
|
||||
const instance = SettingsData.getDesktopWidgetInstance(instanceId);
|
||||
if (!instance)
|
||||
return `DESKTOP_WIDGET_NOT_FOUND: ${instanceId}`;
|
||||
|
||||
const enabledBool = enabled === "true" || enabled === "1";
|
||||
SettingsData.updateDesktopWidgetInstanceConfig(instanceId, {
|
||||
showOnOverlay: enabledBool
|
||||
});
|
||||
return enabledBool ? `DESKTOP_WIDGET_OVERLAY_ENABLED: ${instanceId}` : `DESKTOP_WIDGET_OVERLAY_DISABLED: ${instanceId}`;
|
||||
}
|
||||
|
||||
function list(): string {
|
||||
const instances = SettingsData.desktopWidgetInstances || [];
|
||||
if (instances.length === 0)
|
||||
return "No desktop widgets configured";
|
||||
return instances.map(i => `${i.id} [${i.widgetType}] ${i.name || i.widgetType}`).join("\n");
|
||||
}
|
||||
|
||||
function status(instanceId: string): string {
|
||||
if (!instanceId)
|
||||
return "ERROR: No instance ID specified";
|
||||
|
||||
const instance = SettingsData.getDesktopWidgetInstance(instanceId);
|
||||
if (!instance)
|
||||
return `DESKTOP_WIDGET_NOT_FOUND: ${instanceId}`;
|
||||
|
||||
const overlay = instance.config?.showOnOverlay ?? false;
|
||||
const overview = instance.config?.showOnOverview ?? false;
|
||||
return `overlay: ${overlay}, overview: ${overview}`;
|
||||
}
|
||||
|
||||
target: "desktopWidget"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,17 +46,16 @@ Item {
|
||||
property var customKeyboardFocus: null
|
||||
property bool useOverlayLayer: false
|
||||
readonly property alias contentWindow: contentWindow
|
||||
readonly property alias backgroundWindow: backgroundWindow
|
||||
readonly property alias clickCatcher: clickCatcher
|
||||
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
|
||||
|
||||
readonly property bool useSingleWindow: root.useHyprlandFocusGrab
|
||||
readonly property bool useBackground: showBackground && SettingsData.modalDarkenBackground
|
||||
readonly property bool useSingleWindow: useHyprlandFocusGrab || useBackground
|
||||
|
||||
signal opened
|
||||
signal dialogClosed
|
||||
signal backgroundClicked
|
||||
|
||||
property bool animationsEnabled: true
|
||||
readonly property bool useBackgroundWindow: !useSingleWindow
|
||||
|
||||
function open() {
|
||||
ModalManager.openModal(root);
|
||||
@@ -64,25 +63,21 @@ Item {
|
||||
const focusedScreen = CompositorService.getFocusedScreen();
|
||||
if (focusedScreen) {
|
||||
contentWindow.screen = focusedScreen;
|
||||
if (useBackgroundWindow)
|
||||
backgroundWindow.screen = focusedScreen;
|
||||
if (!useSingleWindow)
|
||||
clickCatcher.screen = focusedScreen;
|
||||
}
|
||||
shouldBeVisible = true;
|
||||
contentWindow.visible = false;
|
||||
if (useBackgroundWindow)
|
||||
backgroundWindow.visible = true;
|
||||
Qt.callLater(() => {
|
||||
contentWindow.visible = true;
|
||||
shouldHaveFocus = false;
|
||||
Qt.callLater(() => {
|
||||
shouldHaveFocus = Qt.binding(() => shouldBeVisible);
|
||||
});
|
||||
});
|
||||
if (!useSingleWindow)
|
||||
clickCatcher.visible = true;
|
||||
contentWindow.visible = true;
|
||||
shouldHaveFocus = false;
|
||||
Qt.callLater(() => shouldHaveFocus = Qt.binding(() => shouldBeVisible));
|
||||
}
|
||||
|
||||
function close() {
|
||||
shouldBeVisible = false;
|
||||
shouldHaveFocus = false;
|
||||
ModalManager.closeModal(root);
|
||||
closeTimer.restart();
|
||||
}
|
||||
|
||||
@@ -90,10 +85,11 @@ Item {
|
||||
animationsEnabled = false;
|
||||
shouldBeVisible = false;
|
||||
shouldHaveFocus = false;
|
||||
ModalManager.closeModal(root);
|
||||
closeTimer.stop();
|
||||
contentWindow.visible = false;
|
||||
if (useBackgroundWindow)
|
||||
backgroundWindow.visible = false;
|
||||
if (!useSingleWindow)
|
||||
clickCatcher.visible = false;
|
||||
dialogClosed();
|
||||
Qt.callLater(() => animationsEnabled = true);
|
||||
}
|
||||
@@ -105,9 +101,8 @@ Item {
|
||||
Connections {
|
||||
target: ModalManager
|
||||
function onCloseAllModalsExcept(excludedModal) {
|
||||
if (excludedModal !== root && !allowStacking && shouldBeVisible) {
|
||||
if (excludedModal !== root && !allowStacking && shouldBeVisible)
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,8 +124,8 @@ Item {
|
||||
const newScreen = CompositorService.getFocusedScreen();
|
||||
if (newScreen) {
|
||||
contentWindow.screen = newScreen;
|
||||
if (useBackgroundWindow)
|
||||
backgroundWindow.screen = newScreen;
|
||||
if (!useSingleWindow)
|
||||
clickCatcher.screen = newScreen;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,12 +134,12 @@ Item {
|
||||
id: closeTimer
|
||||
interval: animationDuration + 120
|
||||
onTriggered: {
|
||||
if (!shouldBeVisible) {
|
||||
contentWindow.visible = false;
|
||||
if (useBackgroundWindow)
|
||||
backgroundWindow.visible = false;
|
||||
dialogClosed();
|
||||
}
|
||||
if (shouldBeVisible)
|
||||
return;
|
||||
contentWindow.visible = false;
|
||||
if (!useSingleWindow)
|
||||
clickCatcher.visible = false;
|
||||
dialogClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,11 +174,11 @@ Item {
|
||||
})(), dpr)
|
||||
|
||||
PanelWindow {
|
||||
id: backgroundWindow
|
||||
id: clickCatcher
|
||||
visible: false
|
||||
color: "transparent"
|
||||
|
||||
WlrLayershell.namespace: root.layerNamespace + ":background"
|
||||
WlrLayershell.namespace: root.layerNamespace + ":clickcatcher"
|
||||
WlrLayershell.layer: WlrLayershell.Top
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
@@ -199,41 +194,16 @@ Item {
|
||||
item: Rectangle {
|
||||
x: root.alignedX
|
||||
y: root.alignedY
|
||||
width: root.shouldBeVisible ? root.alignedWidth : 0
|
||||
height: root.shouldBeVisible ? root.alignedHeight : 0
|
||||
width: root.alignedWidth
|
||||
height: root.alignedHeight
|
||||
}
|
||||
intersection: Intersection.Xor
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.useBackgroundWindow && root.closeOnBackgroundClick && root.shouldBeVisible
|
||||
onClicked: mouse => {
|
||||
const clickX = mouse.x;
|
||||
const clickY = mouse.y;
|
||||
const outsideContent = clickX < root.alignedX || clickX > root.alignedX + root.alignedWidth || clickY < root.alignedY || clickY > root.alignedY + root.alignedHeight;
|
||||
|
||||
if (!outsideContent)
|
||||
return;
|
||||
root.backgroundClicked();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
opacity: root.showBackground && SettingsData.modalDarkenBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
||||
visible: root.useBackgroundWindow && root.showBackground && SettingsData.modalDarkenBackground
|
||||
|
||||
Behavior on opacity {
|
||||
enabled: root.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||
}
|
||||
}
|
||||
enabled: root.closeOnBackgroundClick && root.shouldBeVisible
|
||||
onClicked: root.backgroundClicked()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,8 +243,8 @@ Item {
|
||||
anchors {
|
||||
left: true
|
||||
top: true
|
||||
right: root.useSingleWindow ? true : undefined
|
||||
bottom: root.useSingleWindow ? true : undefined
|
||||
right: root.useSingleWindow
|
||||
bottom: root.useSingleWindow
|
||||
}
|
||||
|
||||
WlrLayershell.margins {
|
||||
@@ -284,8 +254,8 @@ Item {
|
||||
bottom: 0
|
||||
}
|
||||
|
||||
implicitWidth: root.useSingleWindow ? undefined : root.alignedWidth + (shadowBuffer * 2)
|
||||
implicitHeight: root.useSingleWindow ? undefined : root.alignedHeight + (shadowBuffer * 2)
|
||||
implicitWidth: root.useSingleWindow ? 0 : root.alignedWidth + (shadowBuffer * 2)
|
||||
implicitHeight: root.useSingleWindow ? 0 : root.alignedHeight + (shadowBuffer * 2)
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
@@ -309,8 +279,8 @@ Item {
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
color: "black"
|
||||
opacity: root.showBackground && SettingsData.modalDarkenBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
||||
visible: root.useSingleWindow && root.showBackground && SettingsData.modalDarkenBackground
|
||||
opacity: root.useBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
||||
visible: root.useBackground
|
||||
|
||||
Behavior on opacity {
|
||||
enabled: root.animationsEnabled
|
||||
|
||||
@@ -128,6 +128,9 @@ DankModal {
|
||||
FocusScope {
|
||||
id: colorContent
|
||||
|
||||
LayoutMirroring.enabled: I18n.isRtl
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
property alias hexInput: hexInput
|
||||
|
||||
anchors.fill: parent
|
||||
@@ -160,12 +163,14 @@ DankModal {
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Select a color from the palette or use custom sliders")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceTextMedium
|
||||
anchors.left: parent.left
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +365,7 @@ DankModal {
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
GridView {
|
||||
@@ -410,6 +416,7 @@ DankModal {
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -462,6 +469,7 @@ DankModal {
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
@@ -507,6 +515,7 @@ DankModal {
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceTextMedium
|
||||
font.weight: Font.Medium
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -566,6 +575,7 @@ DankModal {
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceTextMedium
|
||||
font.weight: Font.Medium
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -630,6 +640,7 @@ DankModal {
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceTextMedium
|
||||
font.weight: Font.Medium
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
Row {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user