mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-28 23:42:51 -05:00
Compare commits
113 Commits
displaycon
...
9fc0d5efff
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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, '-') }}
|
prerelease: ${{ contains(env.TAG, '-') }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
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:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
|
package:
|
||||||
|
description: 'Package to build (dms, dms-greeter, or both)'
|
||||||
|
required: false
|
||||||
|
default: 'dms'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- dms
|
||||||
|
- dms-greeter
|
||||||
|
- both
|
||||||
version:
|
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
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
release:
|
release:
|
||||||
@@ -13,8 +22,27 @@ on:
|
|||||||
default: '1'
|
default: '1'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-upload:
|
determine-packages:
|
||||||
runs-on: ubuntu-latest
|
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:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -39,7 +67,7 @@ jobs:
|
|||||||
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "release=$RELEASE" >> $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
|
- name: Setup build environment
|
||||||
run: |
|
run: |
|
||||||
@@ -70,157 +98,31 @@ jobs:
|
|||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
RELEASE="${{ steps.version.outputs.release }}"
|
RELEASE="${{ steps.version.outputs.release }}"
|
||||||
CHANGELOG_DATE="$(date '+%a %b %d %Y')"
|
CHANGELOG_DATE="$(date '+%a %b %d %Y')"
|
||||||
|
PACKAGE="${{ matrix.package }}"
|
||||||
|
|
||||||
cat > ~/rpmbuild/SPECS/dms.spec <<'SPECEOF'
|
# Copy spec file from repository
|
||||||
# Spec for DMS stable releases - Generated by GitHub Actions
|
cp distro/fedora/${PACKAGE}.spec ~/rpmbuild/SPECS/${PACKAGE}.spec
|
||||||
|
|
||||||
%global debug_package %{nil}
|
# Replace placeholders with actual values
|
||||||
%global version VERSION_PLACEHOLDER
|
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/${PACKAGE}.spec
|
||||||
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
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
|
echo "✅ Spec file generated for ${PACKAGE} v${VERSION}-${RELEASE}"
|
||||||
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 ""
|
echo ""
|
||||||
echo "=== Spec file preview ==="
|
echo "=== Spec file preview ==="
|
||||||
head -40 ~/rpmbuild/SPECS/dms.spec
|
head -40 ~/rpmbuild/SPECS/${PACKAGE}.spec
|
||||||
|
|
||||||
- name: Build SRPM
|
- name: Build SRPM
|
||||||
id: build
|
id: build
|
||||||
run: |
|
run: |
|
||||||
cd ~/rpmbuild/SPECS
|
cd ~/rpmbuild/SPECS
|
||||||
|
PACKAGE="${{ matrix.package }}"
|
||||||
|
|
||||||
echo "🔨 Building SRPM..."
|
echo "🔨 Building SRPM for ${PACKAGE}..."
|
||||||
rpmbuild -bs dms.spec
|
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")
|
SRPM_NAME=$(basename "$SRPM")
|
||||||
|
|
||||||
echo "srpm_path=$SRPM" >> $GITHUB_OUTPUT
|
echo "srpm_path=$SRPM" >> $GITHUB_OUTPUT
|
||||||
@@ -234,7 +136,7 @@ jobs:
|
|||||||
- name: Upload SRPM artifact
|
- name: Upload SRPM artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: dms-stable-srpm-${{ steps.version.outputs.version }}
|
name: ${{ matrix.package }}-stable-srpm-${{ steps.version.outputs.version }}
|
||||||
path: ${{ steps.build.outputs.srpm_path }}
|
path: ${{ steps.build.outputs.srpm_path }}
|
||||||
retention-days: 90
|
retention-days: 90
|
||||||
|
|
||||||
@@ -255,23 +157,40 @@ jobs:
|
|||||||
|
|
||||||
echo "✅ Copr CLI configured"
|
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
|
- name: Upload to Copr
|
||||||
run: |
|
run: |
|
||||||
SRPM="${{ steps.build.outputs.srpm_path }}"
|
SRPM="${{ steps.build.outputs.srpm_path }}"
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
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 " SRPM: $(basename $SRPM)"
|
||||||
echo " Version: $VERSION"
|
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"
|
echo "$BUILD_OUTPUT"
|
||||||
|
|
||||||
BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -oP 'Build was added to.*\K[0-9]+' || echo "unknown")
|
BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -oP 'Build was added to.*\K[0-9]+' || echo "unknown")
|
||||||
|
|
||||||
if [ "$BUILD_ID" != "unknown" ]; then
|
if [ "$BUILD_ID" != "unknown" ]; then
|
||||||
echo "✅ Build submitted successfully!"
|
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
|
else
|
||||||
echo "⚠️ Could not extract build ID, but upload may have succeeded"
|
echo "⚠️ Could not extract build ID, but upload may have succeeded"
|
||||||
fi
|
fi
|
||||||
@@ -279,10 +198,13 @@ jobs:
|
|||||||
- name: Build summary
|
- name: Build summary
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
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 "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Package:** ${PACKAGE}" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- **Version:** ${{ steps.version.outputs.version }}-${{ steps.version.outputs.release }}" >> $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 "- **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 "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "Stable release has been built and uploaded to Copr!" >> $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)"
|
description: "Package to update (dms, dms-git, or all)"
|
||||||
required: false
|
required: false
|
||||||
default: "all"
|
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:
|
rebuild_release:
|
||||||
description: "Release number for rebuilds (e.g., 2, 3, 4 to increment spec Release)"
|
description: "Release number for rebuilds (e.g., 2, 3, 4 to increment spec Release)"
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 */3 * * *" # Every 3 hours for dms-git builds
|
- cron: "0 */3 * * *" # Every 3 hours for dms-git builds
|
||||||
|
|
||||||
@@ -97,7 +98,7 @@ jobs:
|
|||||||
# Rebuild requested - always proceed
|
# Rebuild requested - always proceed
|
||||||
echo "packages=$PKG" >> $GITHUB_OUTPUT
|
echo "packages=$PKG" >> $GITHUB_OUTPUT
|
||||||
echo "has_updates=true" >> $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
|
elif [[ "$PKG" == "all" ]]; then
|
||||||
# Check each package and build list of those needing updates
|
# Check each package and build list of those needing updates
|
||||||
@@ -161,16 +162,51 @@ jobs:
|
|||||||
id: packages
|
id: packages
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
|
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
|
||||||
|
# Tag push event - use the pushed tag
|
||||||
echo "packages=dms" >> $GITHUB_OUTPUT
|
echo "packages=dms" >> $GITHUB_OUTPUT
|
||||||
VERSION="${GITHUB_REF#refs/tags/}"
|
VERSION="${GITHUB_REF#refs/tags/}"
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "Triggered by tag: $VERSION"
|
echo "Triggered by tag: $VERSION"
|
||||||
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||||
|
# Scheduled run - dms-git only
|
||||||
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
||||||
echo "Triggered by schedule: updating git package"
|
echo "Triggered by schedule: updating git package"
|
||||||
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
||||||
# Use filtered packages from check-updates when package="all" and no rebuild requested
|
# Manual workflow dispatch
|
||||||
if [[ "${{ github.event.inputs.package }}" == "all" ]] && [[ -z "${{ github.event.inputs.rebuild_release }}" ]]; then
|
|
||||||
|
# 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 "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
||||||
echo "Manual trigger: all (filtered to: ${{ needs.check-updates.outputs.packages }})"
|
echo "Manual trigger: all (filtered to: ${{ needs.check-updates.outputs.packages }})"
|
||||||
else
|
else
|
||||||
@@ -186,7 +222,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
||||||
COMMIT_COUNT=$(git rev-list --count 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}"
|
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
|
||||||
echo "📦 Updating dms-git.spec to version: $NEW_VERSION"
|
echo "📦 Updating dms-git.spec to version: $NEW_VERSION"
|
||||||
|
|
||||||
@@ -207,14 +243,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
||||||
COMMIT_COUNT=$(git rev-list --count 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}"
|
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
|
||||||
echo "📦 Updating Debian dms-git changelog to version: $NEW_VERSION"
|
echo "📦 Updating Debian dms-git changelog to version: $NEW_VERSION"
|
||||||
|
|
||||||
# Single changelog entry (git snapshots don't need history)
|
# Single changelog entry (git snapshots don't need history)
|
||||||
CHANGELOG_DATE=$(date -R)
|
CHANGELOG_DATE=$(date -R)
|
||||||
{
|
{
|
||||||
echo "dms-git ($NEW_VERSION) nightly; urgency=medium"
|
echo "dms-git (${NEW_VERSION}db1) nightly; urgency=medium"
|
||||||
echo ""
|
echo ""
|
||||||
echo " * Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)"
|
echo " * Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -226,10 +262,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.packages.outputs.version }}"
|
VERSION="${{ steps.packages.outputs.version }}"
|
||||||
VERSION_NO_V="${VERSION#v}"
|
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
|
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)
|
# Single changelog entry (full history on OBS website)
|
||||||
DATE_STR=$(date "+%a %b %d %Y")
|
DATE_STR=$(date "+%a %b %d %Y")
|
||||||
LOCAL_SPEC_HEAD=$(sed -n '1,/%changelog/{ /%changelog/d; p }' distro/opensuse/dms.spec)
|
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
|
if [[ -f "distro/debian/dms/debian/changelog" ]]; then
|
||||||
CHANGELOG_DATE=$(date -R)
|
CHANGELOG_DATE=$(date -R)
|
||||||
{
|
{
|
||||||
echo "dms ($VERSION_NO_V) stable; urgency=medium"
|
echo "dms (${VERSION_NO_V}db1) stable; urgency=medium"
|
||||||
echo ""
|
echo ""
|
||||||
echo " * Update to $VERSION stable release"
|
echo " * Update to $VERSION stable release"
|
||||||
echo ""
|
echo ""
|
||||||
echo " -- Avenge Media <AvengeMedia.US@gmail.com> $CHANGELOG_DATE"
|
echo " -- Avenge Media <AvengeMedia.US@gmail.com> $CHANGELOG_DATE"
|
||||||
} > "distro/debian/dms/debian/changelog"
|
} > "distro/debian/dms/debian/changelog"
|
||||||
echo "✓ Updated Debian changelog to $VERSION_NO_V"
|
echo "✓ Updated Debian changelog to ${VERSION_NO_V}db1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
@@ -289,6 +330,7 @@ jobs:
|
|||||||
- name: Upload to OBS
|
- name: Upload to OBS
|
||||||
env:
|
env:
|
||||||
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
|
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
|
||||||
|
TAG_VERSION: ${{ steps.packages.outputs.version }}
|
||||||
run: |
|
run: |
|
||||||
PACKAGES="${{ steps.packages.outputs.packages }}"
|
PACKAGES="${{ steps.packages.outputs.packages }}"
|
||||||
|
|
||||||
@@ -300,6 +342,7 @@ jobs:
|
|||||||
MESSAGE="Automated update from GitHub Actions"
|
MESSAGE="Automated update from GitHub Actions"
|
||||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||||
MESSAGE="Update to ${{ steps.packages.outputs.version }}"
|
MESSAGE="Update to ${{ steps.packages.outputs.version }}"
|
||||||
|
echo "==> Version being uploaded: ${{ steps.packages.outputs.version }}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# PACKAGES can be space-separated list (e.g., "dms-git dms" from "all" check)
|
# PACKAGES can be space-separated list (e.g., "dms-git dms" from "all" check)
|
||||||
@@ -309,7 +352,7 @@ jobs:
|
|||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
echo "Uploading $PKG to OBS..."
|
echo "Uploading $PKG to OBS..."
|
||||||
if [[ -n "$REBUILD_RELEASE" ]]; then
|
if [[ -n "$REBUILD_RELEASE" ]]; then
|
||||||
echo "🔄 Using rebuild release number: ppa$REBUILD_RELEASE"
|
echo "🔄 Using rebuild release number: db$REBUILD_RELEASE"
|
||||||
fi
|
fi
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
@@ -350,7 +393,7 @@ jobs:
|
|||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
if [[ -n "${{ github.event.inputs.rebuild_release }}" ]]; then
|
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
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
3
.github/workflows/run-ppa.yml
vendored
3
.github/workflows/run-ppa.yml
vendored
@@ -51,7 +51,8 @@ jobs:
|
|||||||
check_stable_package() {
|
check_stable_package() {
|
||||||
local PKG="$1"
|
local PKG="$1"
|
||||||
local PPA_NAME="$2"
|
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_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]*$//')
|
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
|
go.work.sum
|
||||||
|
|
||||||
# env file
|
# env file
|
||||||
.env
|
.env*
|
||||||
|
|
||||||
# Editor/IDE
|
# Editor/IDE
|
||||||
# .idea/
|
# .idea/
|
||||||
# .vscode/
|
# .vscode/
|
||||||
|
vim/
|
||||||
|
|
||||||
bin/
|
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
|
# 1.2.0
|
||||||
|
|
||||||
- Added clipboard and clipboard history integration
|
- Added clipboard and clipboard history integration
|
||||||
- Added swipe to dismiss notification popups and from center
|
- Added swipe to dismiss notification popups and from center
|
||||||
- Added paste from clipboard history view - requires wtype
|
- Added paste from clipboard history view - requires wtype
|
||||||
- Optimize surface damage of OSD & Toast
|
- 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";
|
inputs.dms.url = "github:AvengeMedia/DankMaterialShell";
|
||||||
|
|
||||||
# Use in home-manager or NixOS configuration
|
# 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]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=DMS Application Picker
|
Name=DMS
|
||||||
Comment=Select an application to open links and files
|
Comment=Select an application to open links and files
|
||||||
Exec=dms open %u
|
Exec=dms open %u
|
||||||
Icon=danklogo
|
Icon=danklogo
|
||||||
Terminal=false
|
Terminal=false
|
||||||
NoDisplay=true
|
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;
|
Categories=Utility;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Type=dbus
|
|||||||
BusName=org.freedesktop.Notifications
|
BusName=org.freedesktop.Notifications
|
||||||
ExecStart=/usr/bin/dms run --session
|
ExecStart=/usr/bin/dms run --session
|
||||||
ExecReload=/usr/bin/pkill -USR1 -x dms
|
ExecReload=/usr/bin/pkill -USR1 -x dms
|
||||||
Restart=always
|
Restart=on-failure
|
||||||
RestartSec=1.23
|
RestartSec=1.23
|
||||||
TimeoutStopSec=10
|
TimeoutStopSec=10
|
||||||
|
|
||||||
|
|||||||
@@ -14,34 +14,63 @@ Distribution-aware installer with TUI for deploying DMS and compositor configura
|
|||||||
|
|
||||||
## System Integration
|
## System Integration
|
||||||
|
|
||||||
**Wayland Protocols**
|
### Wayland Protocols (Client)
|
||||||
- `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
|
|
||||||
|
|
||||||
**DBus Interfaces**
|
All Wayland protocols are consumed as a client - connecting to the compositor.
|
||||||
- NetworkManager/iwd - Network management
|
|
||||||
- logind - Session control and inhibit locks
|
|
||||||
- accountsservice - User account information
|
|
||||||
- CUPS - Printer management
|
|
||||||
- Custom IPC via unix socket (JSON API)
|
|
||||||
|
|
||||||
**Hardware Control**
|
| Protocol | Purpose |
|
||||||
- DDC/CI protocol - External monitor brightness control (like `ddcutil`)
|
| ----------------------------------------- | ----------------------------------------------------------- |
|
||||||
- Backlight control - Internal display brightness via `login1` or sysfs
|
| `wlr-gamma-control-unstable-v1` | Night mode color temperature control |
|
||||||
- LED control - Keyboard/device LED management
|
| `wlr-screencopy-unstable-v1` | Screen capture for color picker/screenshot |
|
||||||
- evdev input monitoring - Keyboard state tracking (caps lock, etc.)
|
| `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 registry integration
|
||||||
- Plugin lifecycle management
|
- Plugin lifecycle management
|
||||||
- Settings persistence
|
- Settings persistence
|
||||||
|
|
||||||
## CLI Commands
|
## CLI Commands
|
||||||
|
|
||||||
- `dms run [-d]` - Start shell (optionally as daemon)
|
- `dms run [-d]` - Start shell (optionally as daemon)
|
||||||
- `dms restart` / `dms kill` - Manage running processes
|
- `dms restart` / `dms kill` - Manage running processes
|
||||||
- `dms ipc <command>` - Send IPC commands (toggle launcher, notifications, etc.)
|
- `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+
|
Requires Go 1.24+
|
||||||
|
|
||||||
**Development build:**
|
**Development build:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make # Build dms CLI
|
make # Build dms CLI
|
||||||
make dankinstall # Build installer
|
make dankinstall # Build installer
|
||||||
@@ -77,6 +107,7 @@ make test # Run tests
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Distribution build:**
|
**Distribution build:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make dist # Build without update/greeter features
|
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`
|
Produces `bin/dms-linux-amd64` and `bin/dms-linux-arm64`
|
||||||
|
|
||||||
**Installation:**
|
**Installation:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo make install # Install to /usr/local/bin/dms
|
sudo make install # Install to /usr/local/bin/dms
|
||||||
```
|
```
|
||||||
@@ -91,6 +123,7 @@ sudo make install # Install to /usr/local/bin/dms
|
|||||||
## Development
|
## Development
|
||||||
|
|
||||||
**Setup pre-commit hooks:**
|
**Setup pre-commit hooks:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git config core.hooksPath .githooks
|
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.
|
This runs gofmt, golangci-lint, tests, and builds before each commit when `core/` files are staged.
|
||||||
|
|
||||||
**Regenerating Wayland Protocol Bindings:**
|
**Regenerating Wayland Protocol Bindings:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go install github.com/rajveermalviya/go-wayland/cmd/go-wayland-scanner@latest
|
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 \
|
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:**
|
**Module Structure:**
|
||||||
|
|
||||||
- `cmd/` - Binary entrypoints (dms, dankinstall)
|
- `cmd/` - Binary entrypoints (dms, dankinstall)
|
||||||
- `internal/distros/` - Distribution-specific installation logic
|
- `internal/distros/` - Distribution-specific installation logic
|
||||||
- `internal/proto/` - Wayland protocol bindings
|
- `internal/proto/` - Wayland protocol bindings
|
||||||
|
|||||||
@@ -144,8 +144,6 @@ var (
|
|||||||
clipConfigEnabled bool
|
clipConfigEnabled bool
|
||||||
clipConfigDisableHistory bool
|
clipConfigDisableHistory bool
|
||||||
clipConfigEnableHistory bool
|
clipConfigEnableHistory bool
|
||||||
clipConfigDisablePersist bool
|
|
||||||
clipConfigEnablePersist bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -173,8 +171,6 @@ func init() {
|
|||||||
clipConfigSetCmd.Flags().BoolVar(&clipConfigEnabled, "enable", false, "Enable clipboard manager")
|
clipConfigSetCmd.Flags().BoolVar(&clipConfigEnabled, "enable", false, "Enable clipboard manager")
|
||||||
clipConfigSetCmd.Flags().BoolVar(&clipConfigDisableHistory, "disable-history", false, "Disable clipboard history persistence")
|
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(&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)")
|
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 {
|
if clipConfigEnableHistory {
|
||||||
params["disableHistory"] = false
|
params["disableHistory"] = false
|
||||||
}
|
}
|
||||||
if clipConfigDisablePersist {
|
|
||||||
params["disablePersist"] = true
|
|
||||||
}
|
|
||||||
if clipConfigEnablePersist {
|
|
||||||
params["disablePersist"] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(params) == 0 {
|
if len(params) == 0 {
|
||||||
fmt.Println("No config options specified")
|
fmt.Println("No config options specified")
|
||||||
|
|||||||
@@ -171,7 +171,6 @@ var pluginsUpdateCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runVersion(cmd *cobra.Command, args []string) {
|
func runVersion(cmd *cobra.Command, args []string) {
|
||||||
printASCII()
|
|
||||||
fmt.Printf("%s\n", formatVersion(Version))
|
fmt.Printf("%s\n", formatVersion(Version))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +219,7 @@ func getBaseVersion() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback
|
// Fallback
|
||||||
return "0.6.2"
|
return "1.0.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
func startDebugServer() error {
|
func startDebugServer() error {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func init() {
|
|||||||
dank16Cmd.Flags().Bool("json", false, "Output in JSON format")
|
dank16Cmd.Flags().Bool("json", false, "Output in JSON format")
|
||||||
dank16Cmd.Flags().Bool("kitty", false, "Output in Kitty terminal format")
|
dank16Cmd.Flags().Bool("kitty", false, "Output in Kitty terminal format")
|
||||||
dank16Cmd.Flags().Bool("foot", false, "Output in Foot 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("alacritty", false, "Output in Alacritty terminal format")
|
||||||
dank16Cmd.Flags().Bool("ghostty", false, "Output in Ghostty terminal format")
|
dank16Cmd.Flags().Bool("ghostty", false, "Output in Ghostty terminal format")
|
||||||
dank16Cmd.Flags().Bool("wezterm", false, "Output in Wezterm 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")
|
isJson, _ := cmd.Flags().GetBool("json")
|
||||||
isKitty, _ := cmd.Flags().GetBool("kitty")
|
isKitty, _ := cmd.Flags().GetBool("kitty")
|
||||||
isFoot, _ := cmd.Flags().GetBool("foot")
|
isFoot, _ := cmd.Flags().GetBool("foot")
|
||||||
|
isNeovim, _ := cmd.Flags().GetBool("neovim")
|
||||||
isAlacritty, _ := cmd.Flags().GetBool("alacritty")
|
isAlacritty, _ := cmd.Flags().GetBool("alacritty")
|
||||||
isGhostty, _ := cmd.Flags().GetBool("ghostty")
|
isGhostty, _ := cmd.Flags().GetBool("ghostty")
|
||||||
isWezterm, _ := cmd.Flags().GetBool("wezterm")
|
isWezterm, _ := cmd.Flags().GetBool("wezterm")
|
||||||
@@ -116,6 +118,8 @@ func runDank16(cmd *cobra.Command, args []string) {
|
|||||||
fmt.Print(dank16.GenerateGhosttyTheme(colors))
|
fmt.Print(dank16.GenerateGhosttyTheme(colors))
|
||||||
} else if isWezterm {
|
} else if isWezterm {
|
||||||
fmt.Print(dank16.GenerateWeztermTheme(colors))
|
fmt.Print(dank16.GenerateWeztermTheme(colors))
|
||||||
|
} else if isNeovim {
|
||||||
|
fmt.Print(dank16.GenerateNeovimTheme(colors))
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(dank16.GenerateGhosttyTheme(colors))
|
fmt.Print(dank16.GenerateGhosttyTheme(colors))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,12 @@ func runOpen(target string) {
|
|||||||
detectedRequestType = "url"
|
detectedRequestType = "url"
|
||||||
}
|
}
|
||||||
log.Infof("Detected HTTP(S) 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 {
|
} else if _, err := os.Stat(target); err == nil {
|
||||||
// Handle local file paths directly (not file:// URIs)
|
// Handle local file paths directly (not file:// URIs)
|
||||||
// Convert to absolute path
|
// Convert to absolute path
|
||||||
@@ -177,7 +183,7 @@ func runOpen(target string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
method := "apppicker.open"
|
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"
|
method = "browser.open"
|
||||||
params["url"] = target
|
params["url"] = target
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,25 @@ import (
|
|||||||
|
|
||||||
type ipcTargets map[string]map[string][]string
|
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
|
var isSessionManaged bool
|
||||||
|
|
||||||
func execDetachedRestart(targetPID int) {
|
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.Stdin = os.Stdin
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
@@ -214,14 +243,28 @@ func runShellInteractive(session bool) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case sig := <-sigChan:
|
case sig := <-sigChan:
|
||||||
// Handle SIGUSR1 restart for non-session managed processes
|
if sig == syscall.SIGUSR1 {
|
||||||
if sig == syscall.SIGUSR1 && !isSessionManaged {
|
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...")
|
log.Infof("Received SIGUSR1, spawning detached restart process...")
|
||||||
execDetachedRestart(os.Getpid())
|
execDetachedRestart(os.Getpid())
|
||||||
// Exit immediately to avoid race conditions with detached restart
|
|
||||||
return
|
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)
|
log.Infof("\nReceived signal %v, shutting down...", sig)
|
||||||
cancel()
|
cancel()
|
||||||
cmd.Process.Signal(syscall.SIGTERM)
|
cmd.Process.Signal(syscall.SIGTERM)
|
||||||
@@ -235,7 +278,7 @@ func runShellInteractive(session bool) {
|
|||||||
cmd.Process.Signal(syscall.SIGTERM)
|
cmd.Process.Signal(syscall.SIGTERM)
|
||||||
}
|
}
|
||||||
os.Remove(socketPath)
|
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)
|
devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error opening /dev/null: %v", err)
|
log.Fatalf("Error opening /dev/null: %v", err)
|
||||||
@@ -440,15 +493,28 @@ func runShellDaemon(session bool) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case sig := <-sigChan:
|
case sig := <-sigChan:
|
||||||
// Handle SIGUSR1 restart for non-session managed processes
|
if sig == syscall.SIGUSR1 {
|
||||||
if sig == syscall.SIGUSR1 && !isSessionManaged {
|
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...")
|
log.Infof("Received SIGUSR1, spawning detached restart process...")
|
||||||
execDetachedRestart(os.Getpid())
|
execDetachedRestart(os.Getpid())
|
||||||
// Exit immediately to avoid race conditions with detached restart
|
|
||||||
return
|
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()
|
cancel()
|
||||||
cmd.Process.Signal(syscall.SIGTERM)
|
cmd.Process.Signal(syscall.SIGTERM)
|
||||||
os.Remove(socketPath)
|
os.Remove(socketPath)
|
||||||
@@ -460,7 +526,7 @@ func runShellDaemon(session bool) {
|
|||||||
cmd.Process.Signal(syscall.SIGTERM)
|
cmd.Process.Signal(syscall.SIGTERM)
|
||||||
}
|
}
|
||||||
os.Remove(socketPath)
|
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 {
|
for _, cfg := range configs {
|
||||||
path := filepath.Join(dmsDir, cfg.name)
|
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 {
|
if err := os.WriteFile(path, []byte(cfg.content), 0644); err != nil {
|
||||||
return fmt.Errorf("failed to write %s: %w", cfg.name, err)
|
return fmt.Errorf("failed to write %s: %w", cfg.name, err)
|
||||||
}
|
}
|
||||||
@@ -265,7 +270,13 @@ func (cd *ConfigDeployer) deployGhosttyConfig() ([]DeploymentResult, error) {
|
|||||||
|
|
||||||
colorResult := DeploymentResult{
|
colorResult := DeploymentResult{
|
||||||
ConfigType: "Ghostty Colors",
|
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 {
|
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) {
|
func TestGhosttyConfigStructure(t *testing.T) {
|
||||||
assert.Contains(t, GhosttyConfig, "window-decoration = false")
|
assert.Contains(t, GhosttyConfig, "window-decoration = false")
|
||||||
assert.Contains(t, GhosttyConfig, "background-opacity = 1.0")
|
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) {
|
func TestGhosttyColorConfigStructure(t *testing.T) {
|
||||||
|
|||||||
@@ -48,4 +48,4 @@ keybind = shift+enter=text:\n
|
|||||||
gtk-single-instance = true
|
gtk-single-instance = true
|
||||||
|
|
||||||
# Dank color generation
|
# 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)
|
p.Color12.Hex, p.Color13.Hex, p.Color14.Hex, p.Color15.Hex)
|
||||||
return result.String()
|
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"
|
terminalCmd = "ghostty"
|
||||||
}
|
}
|
||||||
|
|
||||||
content := fmt.Sprintf(`QT_QPA_PLATFORM=wayland
|
content := fmt.Sprintf(`ELECTRON_OZONE_PLATFORM_HINT=auto
|
||||||
ELECTRON_OZONE_PLATFORM_HINT=auto
|
|
||||||
QT_QPA_PLATFORMTHEME=gtk3
|
|
||||||
QT_QPA_PLATFORMTHEME_QT6=gtk3
|
|
||||||
TERMINAL=%s
|
TERMINAL=%s
|
||||||
`, terminalCmd)
|
`, terminalCmd)
|
||||||
|
|
||||||
@@ -567,12 +564,6 @@ TERMINAL=%s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseDistribution) EnableDMSService(ctx context.Context, wm deps.WindowManager) error {
|
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 {
|
switch wm {
|
||||||
case deps.WindowManagerNiri:
|
case deps.WindowManagerNiri:
|
||||||
if err := exec.CommandContext(ctx, "systemctl", "--user", "add-wants", "niri.service", "dms").Run(); err != nil {
|
if err := exec.CommandContext(ctx, "systemctl", "--user", "add-wants", "niri.service", "dms").Run(); err != nil {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
||||||
@@ -384,6 +385,8 @@ func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Packa
|
|||||||
debianVersion := "Debian_13"
|
debianVersion := "Debian_13"
|
||||||
if osInfo.VersionID == "testing" {
|
if osInfo.VersionID == "testing" {
|
||||||
debianVersion = "Debian_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 {
|
for _, pkg := range obsPkgs {
|
||||||
@@ -427,7 +430,7 @@ func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Packa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add repository
|
// 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{
|
progressChan <- InstallProgressMsg{
|
||||||
Phase: PhaseSystemPackages,
|
Phase: PhaseSystemPackages,
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ func init() {
|
|||||||
Register("opensuse-tumbleweed", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution {
|
Register("opensuse-tumbleweed", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||||
return NewOpenSUSEDistribution(config, logChan)
|
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 {
|
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 {
|
func (o *OpenSUSEDistribution) enableOBSRepos(ctx context.Context, obsPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
|
||||||
enabledRepos := make(map[string]bool)
|
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 {
|
for _, pkg := range obsPkgs {
|
||||||
if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] {
|
if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] {
|
||||||
o.log(fmt.Sprintf("Enabling OBS repository: %s", 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"
|
// RepoURL format: "home:AvengeMedia:danklinux"
|
||||||
repoPath := strings.ReplaceAll(pkg.RepoURL, ":", ":/")
|
repoPath := strings.ReplaceAll(pkg.RepoURL, ":", ":/")
|
||||||
repoName := strings.ReplaceAll(pkg.RepoURL, ":", "-")
|
repoName := strings.ReplaceAll(pkg.RepoURL, ":", "-")
|
||||||
repoURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/openSUSE_Tumbleweed/%s.repo",
|
repoURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/%s/%s.repo",
|
||||||
repoPath, pkg.RepoURL)
|
repoPath, obsDistroVersion, pkg.RepoURL)
|
||||||
|
|
||||||
checkCmd := exec.CommandContext(ctx, "zypper", "repos", repoName)
|
checkCmd := exec.CommandContext(ctx, "zypper", "repos", repoName)
|
||||||
if checkCmd.Run() == nil {
|
if checkCmd.Run() == nil {
|
||||||
|
|||||||
@@ -19,11 +19,12 @@ type DistroInfo struct {
|
|||||||
|
|
||||||
// OSInfo contains complete OS information
|
// OSInfo contains complete OS information
|
||||||
type OSInfo struct {
|
type OSInfo struct {
|
||||||
Distribution DistroInfo
|
Distribution DistroInfo
|
||||||
Version string
|
Version string
|
||||||
VersionID string
|
VersionID string
|
||||||
PrettyName string
|
VersionCodename string
|
||||||
Architecture string
|
PrettyName string
|
||||||
|
Architecture string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOSInfo detects the current OS and returns information about it
|
// GetOSInfo detects the current OS and returns information about it
|
||||||
@@ -72,6 +73,8 @@ func GetOSInfo() (*OSInfo, error) {
|
|||||||
info.VersionID = value
|
info.VersionID = value
|
||||||
case "VERSION":
|
case "VERSION":
|
||||||
info.Version = value
|
info.Version = value
|
||||||
|
case "VERSION_CODENAME":
|
||||||
|
info.VersionCodename = value
|
||||||
case "PRETTY_NAME":
|
case "PRETTY_NAME":
|
||||||
info.PrettyName = value
|
info.PrettyName = value
|
||||||
}
|
}
|
||||||
@@ -100,6 +103,10 @@ func IsUnsupportedDistro(distroID, versionID string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if distroID == "debian" {
|
if distroID == "debian" {
|
||||||
|
// unstable/sid support
|
||||||
|
if versionID == "sid" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if versionID == "" {
|
if versionID == "" {
|
||||||
// debian testing/sid have no version ID
|
// debian testing/sid have no version ID
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -258,7 +258,9 @@ output_path = '%s'
|
|||||||
if !opts.ShouldSkipTemplate("vesktop") {
|
if !opts.ShouldSkipTemplate("vesktop") {
|
||||||
appendConfig(opts, cfgFile, "vesktop", "vesktop.toml")
|
appendConfig(opts, cfgFile, "vesktop", "vesktop.toml")
|
||||||
}
|
}
|
||||||
|
if !opts.ShouldSkipTemplate("equibop") {
|
||||||
|
appendConfig(opts, cfgFile, "equibop", "equibop.toml")
|
||||||
|
}
|
||||||
if !opts.ShouldSkipTemplate("ghostty") {
|
if !opts.ShouldSkipTemplate("ghostty") {
|
||||||
appendTerminalConfig(opts, cfgFile, tmpDir, "ghostty", "ghostty.toml")
|
appendTerminalConfig(opts, cfgFile, tmpDir, "ghostty", "ghostty.toml")
|
||||||
}
|
}
|
||||||
@@ -274,6 +276,9 @@ output_path = '%s'
|
|||||||
if !opts.ShouldSkipTemplate("wezterm") {
|
if !opts.ShouldSkipTemplate("wezterm") {
|
||||||
appendTerminalConfig(opts, cfgFile, tmpDir, "wezterm", "wezterm.toml")
|
appendTerminalConfig(opts, cfgFile, tmpDir, "wezterm", "wezterm.toml")
|
||||||
}
|
}
|
||||||
|
if !opts.ShouldSkipTemplate("nvim") {
|
||||||
|
appendTerminalConfig(opts, cfgFile, tmpDir, "nvim", "neovim.toml")
|
||||||
|
}
|
||||||
|
|
||||||
if !opts.ShouldSkipTemplate("dgop") {
|
if !opts.ShouldSkipTemplate("dgop") {
|
||||||
appendConfig(opts, cfgFile, "dgop", "dgop.toml")
|
appendConfig(opts, cfgFile, "dgop", "dgop.toml")
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ func (i *ZwlrOutputManagerV1) Dispatch(opcode uint32, fd int, data []byte) {
|
|||||||
l := 0
|
l := 0
|
||||||
objectID := client.Uint32(data[l : l+4])
|
objectID := client.Uint32(data[l : l+4])
|
||||||
proxy := i.Context().GetProxy(objectID)
|
proxy := i.Context().GetProxy(objectID)
|
||||||
if proxy == nil {
|
if proxy == nil || proxy.IsZombie() {
|
||||||
head := &ZwlrOutputHeadV1{}
|
head := &ZwlrOutputHeadV1{}
|
||||||
head.SetContext(i.Context())
|
head.SetContext(i.Context())
|
||||||
head.SetID(objectID)
|
head.SetID(objectID)
|
||||||
@@ -723,7 +723,7 @@ func (i *ZwlrOutputHeadV1) Dispatch(opcode uint32, fd int, data []byte) {
|
|||||||
l := 0
|
l := 0
|
||||||
objectID := client.Uint32(data[l : l+4])
|
objectID := client.Uint32(data[l : l+4])
|
||||||
proxy := i.Context().GetProxy(objectID)
|
proxy := i.Context().GetProxy(objectID)
|
||||||
if proxy == nil {
|
if proxy == nil || proxy.IsZombie() {
|
||||||
mode := &ZwlrOutputModeV1{}
|
mode := &ZwlrOutputModeV1{}
|
||||||
mode.SetContext(i.Context())
|
mode.SetContext(i.Context())
|
||||||
mode.SetID(objectID)
|
mode.SetID(objectID)
|
||||||
@@ -761,8 +761,8 @@ func (i *ZwlrOutputHeadV1) Dispatch(opcode uint32, fd int, data []byte) {
|
|||||||
l := 0
|
l := 0
|
||||||
objectID := client.Uint32(data[l : l+4])
|
objectID := client.Uint32(data[l : l+4])
|
||||||
proxy := i.Context().GetProxy(objectID)
|
proxy := i.Context().GetProxy(objectID)
|
||||||
if proxy == nil {
|
if proxy == nil || proxy.IsZombie() {
|
||||||
// Mode not yet registered, create it
|
// Mode not yet registered or zombie, create fresh
|
||||||
mode := &ZwlrOutputModeV1{}
|
mode := &ZwlrOutputModeV1{}
|
||||||
mode.SetContext(i.Context())
|
mode.SetContext(i.Context())
|
||||||
mode.SetID(objectID)
|
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 {
|
if v, ok := req.Params["disableHistory"].(bool); ok {
|
||||||
cfg.DisableHistory = v
|
cfg.DisableHistory = v
|
||||||
}
|
}
|
||||||
if v, ok := req.Params["disablePersist"].(bool); ok {
|
|
||||||
cfg.DisablePersist = v
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.SetConfig(cfg); err != nil {
|
if err := m.SetConfig(cfg); err != nil {
|
||||||
models.RespondError(conn, req.ID, err.Error())
|
models.RespondError(conn, req.ID, err.Error())
|
||||||
|
|||||||
@@ -319,10 +319,6 @@ func (m *Manager) readAndStore(r *os.File, mimeType string) {
|
|||||||
m.storeClipboardEntry(data, mimeType)
|
m.storeClipboardEntry(data, mimeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cfg.DisablePersist {
|
|
||||||
m.persistClipboard([]string{mimeType}, map[string][]byte{mimeType: data})
|
|
||||||
}
|
|
||||||
|
|
||||||
m.updateState()
|
m.updateState()
|
||||||
m.notifySubscribers()
|
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 {
|
func (m *Manager) storeEntry(entry Entry) error {
|
||||||
if m.db == nil {
|
if m.db == nil {
|
||||||
return fmt.Errorf("database not available")
|
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 {
|
func (m *Manager) trimLengthInTx(b *bolt.Bucket) error {
|
||||||
|
if m.config.MaxHistory < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
c := b.Cursor()
|
c := b.Cursor()
|
||||||
var count int
|
var count int
|
||||||
for k, _ := c.Last(); k != nil; k, _ = c.Prev() {
|
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.Infof("Clipboard config reloaded: disableHistory=%v", newCfg.DisableHistory)
|
||||||
log.Info("Clipboard persist disabled, releasing ownership")
|
|
||||||
m.releaseOwnership()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Clipboard config reloaded: disableHistory=%v disablePersist=%v",
|
|
||||||
newCfg.DisableHistory, newCfg.DisablePersist)
|
|
||||||
|
|
||||||
m.updateState()
|
m.updateState()
|
||||||
m.notifySubscribers()
|
m.notifySubscribers()
|
||||||
|
|||||||
@@ -458,7 +458,6 @@ func TestDefaultConfig(t *testing.T) {
|
|||||||
assert.False(t, cfg.ClearAtStartup)
|
assert.False(t, cfg.ClearAtStartup)
|
||||||
assert.False(t, cfg.Disabled)
|
assert.False(t, cfg.Disabled)
|
||||||
assert.False(t, cfg.DisableHistory)
|
assert.False(t, cfg.DisableHistory)
|
||||||
assert.True(t, cfg.DisablePersist)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestManager_PostDelegatesToWlContext(t *testing.T) {
|
func TestManager_PostDelegatesToWlContext(t *testing.T) {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ type Config struct {
|
|||||||
|
|
||||||
Disabled bool `json:"disabled"`
|
Disabled bool `json:"disabled"`
|
||||||
DisableHistory bool `json:"disableHistory"`
|
DisableHistory bool `json:"disableHistory"`
|
||||||
DisablePersist bool `json:"disablePersist"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultConfig() Config {
|
func DefaultConfig() Config {
|
||||||
@@ -30,7 +29,6 @@ func DefaultConfig() Config {
|
|||||||
MaxEntrySize: 5 * 1024 * 1024,
|
MaxEntrySize: 5 * 1024 * 1024,
|
||||||
AutoClearDays: 0,
|
AutoClearDays: 0,
|
||||||
ClearAtStartup: false,
|
ClearAtStartup: false,
|
||||||
DisablePersist: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package cups
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -156,9 +158,42 @@ func (m *Manager) PurgeJobs(printerName string) error {
|
|||||||
return err
|
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) {
|
func (m *Manager) GetDevices() ([]Device, error) {
|
||||||
if m.pkHelper != nil {
|
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()
|
deviceAttrs, err := m.client.GetDevices()
|
||||||
@@ -176,6 +211,9 @@ func (m *Manager) GetDevices() ([]Device, error) {
|
|||||||
ID: getStringAttr(attrs, "device-id"),
|
ID: getStringAttr(attrs, "device-id"),
|
||||||
Location: getStringAttr(attrs, "device-location"),
|
Location: getStringAttr(attrs, "device-location"),
|
||||||
}
|
}
|
||||||
|
if device.Class == "network" {
|
||||||
|
device.IP = resolveIPFromURI(uri)
|
||||||
|
}
|
||||||
devices = append(devices, device)
|
devices = append(devices, device)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ type Device struct {
|
|||||||
MakeModel string `json:"makeModel"`
|
MakeModel string `json:"makeModel"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Location string `json:"location"`
|
Location string `json:"location"`
|
||||||
|
IP string `json:"ip,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PPD struct {
|
type PPD struct {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/network"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/network"
|
||||||
serverPlugins "github.com/AvengeMedia/DankMaterialShell/core/internal/server/plugins"
|
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/wayland"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput"
|
||||||
)
|
)
|
||||||
@@ -37,6 +38,11 @@ func RouteRequest(conn net.Conn, req models.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(req.Method, "themes.") {
|
||||||
|
serverThemes.HandleRequest(conn, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(req.Method, "loginctl.") {
|
if strings.HasPrefix(req.Method, "loginctl.") {
|
||||||
if loginctlManager == nil {
|
if loginctlManager == nil {
|
||||||
models.RespondError(conn, req.ID, "loginctl manager not initialized")
|
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 {
|
if v, ok := req.Params["disableHistory"].(bool); ok {
|
||||||
cfg.DisableHistory = v
|
cfg.DisableHistory = v
|
||||||
}
|
}
|
||||||
if v, ok := req.Params["disablePersist"].(bool); ok {
|
|
||||||
cfg.DisablePersist = v
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := clipboard.SaveConfig(cfg); err != nil {
|
if err := clipboard.SaveConfig(cfg); err != nil {
|
||||||
models.RespondError(conn, req.ID, err.Error())
|
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) {
|
handle.SetNameHandler(func(e wlr_output_management.ZwlrOutputHeadV1NameEvent) {
|
||||||
log.Debugf("WlrOutput: Head %d name: %s", headID, e.Name)
|
log.Debugf("WlrOutput: Head %d name: %s", headID, e.Name)
|
||||||
head.name = e.Name
|
head.name = e.Name
|
||||||
|
head.ready = true
|
||||||
m.post(func() {
|
m.post(func() {
|
||||||
m.updateState()
|
m.updateState()
|
||||||
})
|
})
|
||||||
@@ -240,6 +241,7 @@ func (m *Manager) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEven
|
|||||||
handle.SetAdaptiveSyncHandler(func(e wlr_output_management.ZwlrOutputHeadV1AdaptiveSyncEvent) {
|
handle.SetAdaptiveSyncHandler(func(e wlr_output_management.ZwlrOutputHeadV1AdaptiveSyncEvent) {
|
||||||
log.Debugf("WlrOutput: Head %d adaptive sync: %d", headID, e.State)
|
log.Debugf("WlrOutput: Head %d adaptive sync: %d", headID, e.State)
|
||||||
head.adaptiveSync = e.State
|
head.adaptiveSync = e.State
|
||||||
|
head.adaptiveSyncSupported = true
|
||||||
m.post(func() {
|
m.post(func() {
|
||||||
m.updateState()
|
m.updateState()
|
||||||
})
|
})
|
||||||
@@ -251,11 +253,11 @@ func (m *Manager) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEven
|
|||||||
|
|
||||||
m.heads.Delete(headID)
|
m.heads.Delete(headID)
|
||||||
|
|
||||||
m.post(func() {
|
m.wlMutex.Lock()
|
||||||
m.wlMutex.Lock()
|
handle.Release()
|
||||||
handle.Release()
|
m.wlMutex.Unlock()
|
||||||
m.wlMutex.Unlock()
|
|
||||||
|
|
||||||
|
m.post(func() {
|
||||||
m.updateState()
|
m.updateState()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -310,11 +312,11 @@ func (m *Manager) handleMode(headID uint32, e wlr_output_management.ZwlrOutputHe
|
|||||||
|
|
||||||
m.modes.Delete(modeID)
|
m.modes.Delete(modeID)
|
||||||
|
|
||||||
m.post(func() {
|
m.wlMutex.Lock()
|
||||||
m.wlMutex.Lock()
|
handle.Release()
|
||||||
handle.Release()
|
m.wlMutex.Unlock()
|
||||||
m.wlMutex.Unlock()
|
|
||||||
|
|
||||||
|
m.post(func() {
|
||||||
m.updateState()
|
m.updateState()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -328,6 +330,10 @@ func (m *Manager) updateState() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !head.ready {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
modes := make([]OutputMode, 0)
|
modes := make([]OutputMode, 0)
|
||||||
var currentMode *OutputMode
|
var currentMode *OutputMode
|
||||||
|
|
||||||
@@ -355,22 +361,23 @@ func (m *Manager) updateState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
output := Output{
|
output := Output{
|
||||||
Name: head.name,
|
Name: head.name,
|
||||||
Description: head.description,
|
Description: head.description,
|
||||||
Make: head.make,
|
Make: head.make,
|
||||||
Model: head.model,
|
Model: head.model,
|
||||||
SerialNumber: head.serialNumber,
|
SerialNumber: head.serialNumber,
|
||||||
PhysicalWidth: head.physicalWidth,
|
PhysicalWidth: head.physicalWidth,
|
||||||
PhysicalHeight: head.physicalHeight,
|
PhysicalHeight: head.physicalHeight,
|
||||||
Enabled: head.enabled,
|
Enabled: head.enabled,
|
||||||
X: head.x,
|
X: head.x,
|
||||||
Y: head.y,
|
Y: head.y,
|
||||||
Transform: head.transform,
|
Transform: head.transform,
|
||||||
Scale: head.scale,
|
Scale: head.scale,
|
||||||
CurrentMode: currentMode,
|
CurrentMode: currentMode,
|
||||||
Modes: modes,
|
Modes: modes,
|
||||||
AdaptiveSync: head.adaptiveSync,
|
AdaptiveSync: head.adaptiveSync,
|
||||||
ID: head.id,
|
AdaptiveSyncSupported: head.adaptiveSyncSupported,
|
||||||
|
ID: head.id,
|
||||||
}
|
}
|
||||||
outputs = append(outputs, output)
|
outputs = append(outputs, output)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -17,22 +17,23 @@ type OutputMode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Output struct {
|
type Output struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Make string `json:"make"`
|
Make string `json:"make"`
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
SerialNumber string `json:"serialNumber"`
|
SerialNumber string `json:"serialNumber"`
|
||||||
PhysicalWidth int32 `json:"physicalWidth"`
|
PhysicalWidth int32 `json:"physicalWidth"`
|
||||||
PhysicalHeight int32 `json:"physicalHeight"`
|
PhysicalHeight int32 `json:"physicalHeight"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
X int32 `json:"x"`
|
X int32 `json:"x"`
|
||||||
Y int32 `json:"y"`
|
Y int32 `json:"y"`
|
||||||
Transform int32 `json:"transform"`
|
Transform int32 `json:"transform"`
|
||||||
Scale float64 `json:"scale"`
|
Scale float64 `json:"scale"`
|
||||||
CurrentMode *OutputMode `json:"currentMode"`
|
CurrentMode *OutputMode `json:"currentMode"`
|
||||||
Modes []OutputMode `json:"modes"`
|
Modes []OutputMode `json:"modes"`
|
||||||
AdaptiveSync uint32 `json:"adaptiveSync"`
|
AdaptiveSync uint32 `json:"adaptiveSync"`
|
||||||
ID uint32 `json:"id"`
|
AdaptiveSyncSupported bool `json:"adaptiveSyncSupported"`
|
||||||
|
ID uint32 `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
@@ -72,24 +73,26 @@ type Manager struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type headState struct {
|
type headState struct {
|
||||||
id uint32
|
id uint32
|
||||||
handle *wlr_output_management.ZwlrOutputHeadV1
|
handle *wlr_output_management.ZwlrOutputHeadV1
|
||||||
name string
|
name string
|
||||||
description string
|
description string
|
||||||
make string
|
make string
|
||||||
model string
|
model string
|
||||||
serialNumber string
|
serialNumber string
|
||||||
physicalWidth int32
|
physicalWidth int32
|
||||||
physicalHeight int32
|
physicalHeight int32
|
||||||
enabled bool
|
enabled bool
|
||||||
x int32
|
x int32
|
||||||
y int32
|
y int32
|
||||||
transform int32
|
transform int32
|
||||||
scale float64
|
scale float64
|
||||||
currentModeID uint32
|
currentModeID uint32
|
||||||
modeIDs []uint32
|
modeIDs []uint32
|
||||||
adaptiveSync uint32
|
adaptiveSync uint32
|
||||||
finished bool
|
adaptiveSyncSupported bool
|
||||||
|
finished bool
|
||||||
|
ready bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type modeState struct {
|
type modeState struct {
|
||||||
@@ -168,7 +171,7 @@ func stateChanged(old, new *State) bool {
|
|||||||
if oldOut.Transform != newOut.Transform || oldOut.Scale != newOut.Scale {
|
if oldOut.Transform != newOut.Transform || oldOut.Scale != newOut.Scale {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if oldOut.AdaptiveSync != newOut.AdaptiveSync {
|
if oldOut.AdaptiveSync != newOut.AdaptiveSync || oldOut.AdaptiveSyncSupported != newOut.AdaptiveSyncSupported {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (oldOut.CurrentMode == nil) != (newOut.CurrentMode == nil) {
|
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)
|
* 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
|
-- 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
|
dms-git (1.0.0+git2419.993f14a3) nightly; urgency=medium
|
||||||
|
|
||||||
|
|||||||
@@ -3,19 +3,19 @@
|
|||||||
<service name="download_url">
|
<service name="download_url">
|
||||||
<param name="protocol">https</param>
|
<param name="protocol">https</param>
|
||||||
<param name="host">github.com</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>
|
<param name="filename">dms-source.tar.gz</param>
|
||||||
</service>
|
</service>
|
||||||
<!-- Download amd64 binary -->
|
<!-- Download amd64 binary -->
|
||||||
<service name="download_url">
|
<service name="download_url">
|
||||||
<param name="protocol">https</param>
|
<param name="protocol">https</param>
|
||||||
<param name="host">github.com</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>
|
</service>
|
||||||
<!-- Download arm64 binary -->
|
<!-- Download arm64 binary -->
|
||||||
<service name="download_url">
|
<service name="download_url">
|
||||||
<param name="protocol">https</param>
|
<param name="protocol">https</param>
|
||||||
<param name="host">github.com</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>
|
</service>
|
||||||
</services>
|
</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
|
-- 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
|
Package: dms
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
quickshell-git | quickshell,
|
quickshell | quickshell-git,
|
||||||
accountsservice,
|
accountsservice,
|
||||||
cava,
|
cava,
|
||||||
cliphist,
|
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 debug_package %{nil}
|
||||||
%global version {{{ git_repo_version }}}
|
%global version VERSION_PLACEHOLDER
|
||||||
%global pkg_summary DankMaterialShell greeter for greetd
|
%global pkg_summary DankMaterialShell greeter for greetd
|
||||||
|
|
||||||
Name: dms-greeter
|
Name: dms-greeter
|
||||||
Version: %{version}
|
Version: %{version}
|
||||||
Release: 0.git%{?dist}
|
Release: RELEASE_PLACEHOLDER%{?dist}
|
||||||
Summary: %{pkg_summary}
|
Summary: %{pkg_summary}
|
||||||
|
|
||||||
License: MIT
|
License: MIT
|
||||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
VCS: {{{ git_repo_vcs }}}
|
|
||||||
Source0: {{{ git_repo_pack }}}
|
|
||||||
|
|
||||||
BuildRequires: git-core
|
Source0: dms-qml.tar.gz
|
||||||
# For the _tmpfilesdir macro.
|
|
||||||
BuildRequires: systemd-rpm-macros
|
BuildRequires: gzip
|
||||||
|
BuildRequires: wget
|
||||||
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
Requires: greetd
|
Requires: greetd
|
||||||
Requires: (quickshell-git or quickshell)
|
Requires: (quickshell-git or quickshell)
|
||||||
@@ -24,14 +24,11 @@ Requires(post): /usr/sbin/useradd
|
|||||||
Requires(post): /usr/sbin/groupadd
|
Requires(post): /usr/sbin/groupadd
|
||||||
|
|
||||||
Recommends: policycoreutils-python-utils
|
Recommends: policycoreutils-python-utils
|
||||||
Recommends: setfacl
|
Recommends: acl
|
||||||
Suggests: niri
|
Suggests: niri
|
||||||
Suggests: hyprland
|
Suggests: hyprland
|
||||||
Suggests: sway
|
Suggests: sway
|
||||||
|
|
||||||
# Provides: greetd-dms-greeter = %{version}-%{release}
|
|
||||||
# Conflicts: greetd-dms-greeter
|
|
||||||
|
|
||||||
%description
|
%description
|
||||||
DankMaterialShell greeter for greetd login manager. A modern, Material Design 3
|
DankMaterialShell greeter for greetd login manager. A modern, Material Design 3
|
||||||
inspired greeter interface built with Quickshell for Wayland compositors.
|
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.
|
authentication, and dynamic theming.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
{{{ git_repo_setup_macro }}}
|
%setup -q -c -n dms-qml
|
||||||
|
|
||||||
|
%build
|
||||||
|
|
||||||
%install
|
%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
|
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 %{_builddir}/dms-qml/Modules/Greetd/assets/dms-greeter %{buildroot}%{_bindir}/dms-greeter
|
||||||
install -Dm755 quickshell/Modules/Greetd/assets/dms-greeter %{buildroot}%{_bindir}/dms-greeter
|
|
||||||
|
|
||||||
# Install documentation
|
install -Dm644 %{_builddir}/dms-qml/Modules/Greetd/README.md %{buildroot}%{_docdir}/dms-greeter/README.md
|
||||||
install -Dm644 quickshell/Modules/Greetd/README.md %{buildroot}%{_docdir}/dms-greeter/README.md
|
|
||||||
|
|
||||||
# Create cache directory for greeter data
|
install -Dpm0644 %{_builddir}/dms-qml/systemd/tmpfiles-dms-greeter.conf %{buildroot}%{_tmpfilesdir}/dms-greeter.conf
|
||||||
install -Dpm0644 quickshell/systemd/tmpfiles-dms-greeter.conf %{buildroot}%{_tmpfilesdir}/dms-greeter.conf
|
|
||||||
|
|
||||||
# Install LICENSE file
|
install -Dm644 %{_builddir}/dms-qml/LICENSE %{buildroot}%{_docdir}/dms-greeter/LICENSE
|
||||||
install -Dm644 LICENSE %{buildroot}%{_docdir}/dms-greeter/LICENSE
|
|
||||||
|
|
||||||
# Create greeter home directory
|
|
||||||
install -dm755 %{buildroot}%{_sharedstatedir}/greeter
|
install -dm755 %{buildroot}%{_sharedstatedir}/greeter
|
||||||
|
|
||||||
# Note: We do NOT install a PAM config here to avoid conflicting with greetd package
|
# Note: We do NOT install a PAM config here to avoid conflicting w/greetd packages
|
||||||
# Instead, we verify/fix it in %post if needed
|
|
||||||
|
|
||||||
# Remove build and development files
|
# Remove build and development files
|
||||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/.git*
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/.git*
|
||||||
rm -f %{buildroot}%{_datadir}/quickshell/dms-greeter/.gitignore
|
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
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms-greeter/distro
|
||||||
|
|
||||||
%posttrans
|
%posttrans
|
||||||
# Clean up old installation path from previous versions (only if empty)
|
|
||||||
if [ -d "%{_sysconfdir}/xdg/quickshell/dms-greeter" ]; then
|
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/dms-greeter" 2>/dev/null || true
|
||||||
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||||
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||||
@@ -89,7 +80,7 @@ fi
|
|||||||
%{_tmpfilesdir}/%{name}.conf
|
%{_tmpfilesdir}/%{name}.conf
|
||||||
|
|
||||||
%pre
|
%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 group greeter >/dev/null || groupadd -r greeter
|
||||||
getent passwd greeter >/dev/null || \
|
getent passwd greeter >/dev/null || \
|
||||||
useradd -r -g greeter -d %{_sharedstatedir}/greeter -s /bin/bash \
|
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
|
# Verify PAM configuration - only fix if insufficient
|
||||||
PAM_CONFIG="/etc/pam.d/greetd"
|
PAM_CONFIG="/etc/pam.d/greetd"
|
||||||
if [ ! -f "$PAM_CONFIG" ]; then
|
if [ ! -f "$PAM_CONFIG" ]; then
|
||||||
# PAM config doesn't exist - create it
|
|
||||||
cat > "$PAM_CONFIG" << 'PAM_EOF'
|
cat > "$PAM_CONFIG" << 'PAM_EOF'
|
||||||
#%PAM-1.0
|
#%PAM-1.0
|
||||||
auth substack system-auth
|
auth substack system-auth
|
||||||
@@ -149,7 +139,6 @@ PAM_EOF
|
|||||||
# Only show message on initial install
|
# Only show message on initial install
|
||||||
[ "$1" -eq 1 ] && echo "Created PAM configuration for greetd"
|
[ "$1" -eq 1 ] && echo "Created PAM configuration for greetd"
|
||||||
elif ! grep -q "pam_systemd\|system-auth" "$PAM_CONFIG"; then
|
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"
|
cp "$PAM_CONFIG" "$PAM_CONFIG.backup-dms-greeter"
|
||||||
cat > "$PAM_CONFIG" << 'PAM_EOF'
|
cat > "$PAM_CONFIG" << 'PAM_EOF'
|
||||||
#%PAM-1.0
|
#%PAM-1.0
|
||||||
@@ -198,9 +187,8 @@ command = "/usr/bin/dms-greeter --command COMPOSITOR_PLACEHOLDER"
|
|||||||
GREETD_EOF
|
GREETD_EOF
|
||||||
sed -i "s|COMPOSITOR_PLACEHOLDER|$COMPOSITOR|" "$GREETD_CONFIG"
|
sed -i "s|COMPOSITOR_PLACEHOLDER|$COMPOSITOR|" "$GREETD_CONFIG"
|
||||||
CONFIG_STATUS="Created new config with $COMPOSITOR ✓"
|
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
|
elif ! grep -q "dms-greeter" "$GREETD_CONFIG"; then
|
||||||
# Backup existing config
|
|
||||||
BACKUP_FILE="${GREETD_CONFIG}.backup-$(date +%%Y%%m%%d-%%H%%M%%S)"
|
BACKUP_FILE="${GREETD_CONFIG}.backup-$(date +%%Y%%m%%d-%%H%%M%%S)"
|
||||||
cp "$GREETD_CONFIG" "$BACKUP_FILE" 2>/dev/null || true
|
cp "$GREETD_CONFIG" "$BACKUP_FILE" 2>/dev/null || true
|
||||||
|
|
||||||
@@ -267,4 +255,6 @@ if [ "$1" -eq 0 ] && [ -x /usr/sbin/semanage ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
%changelog
|
%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 debug_package %{nil}
|
||||||
%global version {{{ git_repo_version }}}
|
%global version VERSION_PLACEHOLDER
|
||||||
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||||
|
|
||||||
Name: dms
|
Name: dms
|
||||||
Epoch: 2
|
|
||||||
Version: %{version}
|
Version: %{version}
|
||||||
Release: 1%{?dist}
|
Release: RELEASE_PLACEHOLDER%{?dist}
|
||||||
Summary: %{pkg_summary}
|
Summary: %{pkg_summary}
|
||||||
|
|
||||||
License: MIT
|
License: MIT
|
||||||
URL: https://github.com/AvengeMedia/DankMaterialShell
|
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: gzip
|
||||||
BuildRequires: golang >= 1.24
|
|
||||||
BuildRequires: make
|
|
||||||
BuildRequires: wget
|
BuildRequires: wget
|
||||||
BuildRequires: systemd-rpm-macros
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
# Core requirements
|
Requires: (quickshell or quickshell-git)
|
||||||
Requires: (quickshell-git or quickshell)
|
|
||||||
Requires: accountsservice
|
Requires: accountsservice
|
||||||
Requires: dms-cli = %{epoch}:%{version}-%{release}
|
Requires: dms-cli = %{version}-%{release}
|
||||||
Requires: dgop
|
Requires: dgop
|
||||||
|
|
||||||
# Core utilities (Highly recommended for DMS functionality)
|
|
||||||
Recommends: cava
|
Recommends: cava
|
||||||
Recommends: cliphist
|
Recommends: cliphist
|
||||||
Recommends: danksearch
|
Recommends: danksearch
|
||||||
Recommends: matugen
|
Recommends: matugen
|
||||||
Recommends: quickshell-git
|
|
||||||
Recommends: wl-clipboard
|
Recommends: wl-clipboard
|
||||||
|
|
||||||
# Recommended system packages
|
|
||||||
Recommends: NetworkManager
|
Recommends: NetworkManager
|
||||||
Recommends: qt6-qtmultimedia
|
Recommends: qt6-qtmultimedia
|
||||||
Suggests: qt6ct
|
Suggests: qt6ct
|
||||||
|
|
||||||
%description
|
%description
|
||||||
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
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.
|
app launcher, wallpaper customization, and fully customizable with plugins.
|
||||||
|
|
||||||
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
|
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.
|
Provides native DBus bindings, NetworkManager integration, and system utilities.
|
||||||
|
|
||||||
%prep
|
%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
|
case "%{_arch}" in
|
||||||
x86_64)
|
x86_64)
|
||||||
DMS_BINARY="dms-linux-amd64"
|
ARCH_SUFFIX="amd64"
|
||||||
;;
|
;;
|
||||||
aarch64)
|
aarch64)
|
||||||
DMS_BINARY="dms-linux-arm64"
|
ARCH_SUFFIX="arm64"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unsupported architecture: %{_arch}"
|
echo "Unsupported architecture: %{_arch}"
|
||||||
@@ -85,27 +66,35 @@ case "%{_arch}" in
|
|||||||
;;
|
;;
|
||||||
esac
|
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
|
# Shell completions
|
||||||
install -d %{buildroot}%{_datadir}/bash-completion/completions
|
install -d %{buildroot}%{_datadir}/bash-completion/completions
|
||||||
install -d %{buildroot}%{_datadir}/zsh/site-functions
|
install -d %{buildroot}%{_datadir}/zsh/site-functions
|
||||||
install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
||||||
core/bin/${DMS_BINARY} completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
%{_builddir}/dms-cli completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||||
core/bin/${DMS_BINARY} completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
%{_builddir}/dms-cli 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 fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||||
|
|
||||||
# Install systemd user service
|
install -Dm644 %{_builddir}/dms-qml/assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.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 %{_builddir}/dms-qml/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/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||||
|
|
||||||
# Install shell files to shared data location
|
|
||||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
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 -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||||
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||||
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||||
@@ -119,8 +108,7 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
|
|||||||
|
|
||||||
%files
|
%files
|
||||||
%license LICENSE
|
%license LICENSE
|
||||||
%doc CONTRIBUTING.md
|
%doc README.md CONTRIBUTING.md
|
||||||
%doc quickshell/README.md
|
|
||||||
%{_datadir}/quickshell/dms/
|
%{_datadir}/quickshell/dms/
|
||||||
%{_userunitdir}/dms.service
|
%{_userunitdir}/dms.service
|
||||||
%{_datadir}/applications/dms-open.desktop
|
%{_datadir}/applications/dms-open.desktop
|
||||||
@@ -133,4 +121,6 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
|
|||||||
%{_datadir}/fish/vendor_completions.d/dms.fish
|
%{_datadir}/fish/vendor_completions.d/dms.fish
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
{{{ git_repo_changelog }}}
|
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-RELEASE_PLACEHOLDER
|
||||||
|
- Stable release VERSION_PLACEHOLDER
|
||||||
|
- Built from GitHub release
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.programs.dankMaterialShell;
|
cfg = config.programs.dank-material-shell;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages = [
|
packages = [
|
||||||
|
|||||||
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
|
let
|
||||||
inherit (lib) types;
|
inherit (lib) types;
|
||||||
cfg = config.programs.dankMaterialShell.greeter;
|
cfg = config.programs.dank-material-shell.greeter;
|
||||||
|
|
||||||
inherit (config.services.greetd.settings.default_session) user;
|
inherit (config.services.greetd.settings.default_session) user;
|
||||||
|
|
||||||
@@ -44,19 +44,20 @@ in
|
|||||||
{
|
{
|
||||||
imports =
|
imports =
|
||||||
let
|
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
|
in
|
||||||
[
|
[
|
||||||
(lib.mkRemovedOptionModule [
|
(lib.mkRemovedOptionModule [
|
||||||
"programs"
|
"programs"
|
||||||
"dankMaterialShell"
|
"dank-material-shell"
|
||||||
"greeter"
|
"greeter"
|
||||||
"compositor"
|
"compositor"
|
||||||
"extraConfig"
|
"extraConfig"
|
||||||
] msg)
|
] msg)
|
||||||
|
./dms-rename.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
options.programs.dankMaterialShell.greeter = {
|
options.programs.dank-material-shell.greeter = {
|
||||||
enable = lib.mkEnableOption "DankMaterialShell greeter";
|
enable = lib.mkEnableOption "DankMaterialShell greeter";
|
||||||
compositor.name = lib.mkOption {
|
compositor.name = lib.mkOption {
|
||||||
type = types.enum [
|
type = types.enum [
|
||||||
@@ -177,7 +178,7 @@ in
|
|||||||
mv dms-colors.json colors.json || :
|
mv dms-colors.json colors.json || :
|
||||||
chown ${user}: * || :
|
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}/.config/DankMaterialShell/settings.json"
|
||||||
"${cfg.configHome}/.local/state/DankMaterialShell/session.json"
|
"${cfg.configHome}/.local/state/DankMaterialShell/session.json"
|
||||||
"${cfg.configHome}/.cache/DankMaterialShell/dms-colors.json"
|
"${cfg.configHome}/.cache/DankMaterialShell/dms-colors.json"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
...
|
...
|
||||||
}@args:
|
}@args:
|
||||||
let
|
let
|
||||||
cfg = config.programs.dankMaterialShell;
|
cfg = config.programs.dank-material-shell;
|
||||||
jsonFormat = pkgs.formats.json { };
|
jsonFormat = pkgs.formats.json { };
|
||||||
common = import ./common.nix {
|
common = import ./common.nix {
|
||||||
inherit
|
inherit
|
||||||
@@ -22,16 +22,16 @@ in
|
|||||||
(import ./options.nix args)
|
(import ./options.nix args)
|
||||||
(lib.mkRemovedOptionModule [
|
(lib.mkRemovedOptionModule [
|
||||||
"programs"
|
"programs"
|
||||||
"dankMaterialShell"
|
"dank-material-shell"
|
||||||
"enableNightMode"
|
"enableNightMode"
|
||||||
] "Night mode is now always available.")
|
] "Night mode is now always available.")
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "programs" "dankMaterialShell" "enableSystemd" ]
|
[ "programs" "dank-material-shell" "enableSystemd" ]
|
||||||
[ "programs" "dankMaterialShell" "systemd" "enable" ]
|
[ "programs" "dank-material-shell" "systemd" "enable" ]
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
options.programs.dankMaterialShell = with lib.types; {
|
options.programs.dank-material-shell = with lib.types; {
|
||||||
default = {
|
default = {
|
||||||
settings = lib.mkOption {
|
settings = lib.mkOption {
|
||||||
type = jsonFormat.type;
|
type = jsonFormat.type;
|
||||||
|
|||||||
@@ -4,10 +4,14 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.programs.dankMaterialShell;
|
cfg = config.programs.dank-material-shell;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.programs.dankMaterialShell = {
|
imports = [
|
||||||
|
./dms-rename.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
options.programs.dank-material-shell = {
|
||||||
niri = {
|
niri = {
|
||||||
enableKeybinds = lib.mkEnableOption "DankMaterialShell niri keybinds";
|
enableKeybinds = lib.mkEnableOption "DankMaterialShell niri keybinds";
|
||||||
enableSpawn = lib.mkEnableOption "DankMaterialShell niri spawn-at-startup";
|
enableSpawn = lib.mkEnableOption "DankMaterialShell niri spawn-at-startup";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
...
|
...
|
||||||
}@args:
|
}@args:
|
||||||
let
|
let
|
||||||
cfg = config.programs.dankMaterialShell;
|
cfg = config.programs.dank-material-shell;
|
||||||
common = import ./common.nix {
|
common = import ./common.nix {
|
||||||
inherit
|
inherit
|
||||||
config
|
config
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ let
|
|||||||
inherit (lib) types;
|
inherit (lib) types;
|
||||||
path = [
|
path = [
|
||||||
"programs"
|
"programs"
|
||||||
"dankMaterialShell"
|
"dank-material-shell"
|
||||||
];
|
];
|
||||||
|
|
||||||
builtInRemovedMsg = "This is now built-in in DMS and doesn't need additional dependencies.";
|
builtInRemovedMsg = "This is now built-in in DMS and doesn't need additional dependencies.";
|
||||||
@@ -20,16 +20,17 @@ in
|
|||||||
(lib.mkRemovedOptionModule (
|
(lib.mkRemovedOptionModule (
|
||||||
path ++ [ "enableSystemSound" ]
|
path ++ [ "enableSystemSound" ]
|
||||||
) "qtmultimedia is now included on dms-shell package.")
|
) "qtmultimedia is now included on dms-shell package.")
|
||||||
|
./dms-rename.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
options.programs.dankMaterialShell = {
|
options.programs.dank-material-shell = {
|
||||||
enable = lib.mkEnableOption "DankMaterialShell";
|
enable = lib.mkEnableOption "DankMaterialShell";
|
||||||
systemd = {
|
systemd = {
|
||||||
enable = lib.mkEnableOption "DankMaterialShell systemd startup";
|
enable = lib.mkEnableOption "DankMaterialShell systemd startup";
|
||||||
restartIfChanged = lib.mkOption {
|
restartIfChanged = lib.mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = "Auto-restart dms.service when dankMaterialShell changes";
|
description = "Auto-restart dms.service when dank-material-shell changes";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
enableSystemMonitoring = lib.mkOption {
|
enableSystemMonitoring = lib.mkOption {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
%global debug_package %{nil}
|
%global debug_package %{nil}
|
||||||
|
|
||||||
Name: dms
|
Name: dms
|
||||||
Version: 1.0.2
|
Version: 1.0.3
|
||||||
Release: 7%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
Summary: DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||||
|
|
||||||
License: MIT
|
License: MIT
|
||||||
@@ -17,7 +17,7 @@ BuildRequires: gzip
|
|||||||
BuildRequires: systemd-rpm-macros
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
# Core requirements
|
# Core requirements
|
||||||
Requires: (quickshell-git or quickshell)
|
Requires: (quickshell or quickshell-git)
|
||||||
Requires: accountsservice
|
Requires: accountsservice
|
||||||
Requires: dgop
|
Requires: dgop
|
||||||
|
|
||||||
@@ -105,6 +105,9 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
|
|||||||
%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
||||||
|
|
||||||
%changelog
|
%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
|
* Fri Dec 12 2025 AvengeMedia <maintainer@avengemedia.com> - 1.0.2-1
|
||||||
- Update to stable v1.0.2 release
|
- Update to stable v1.0.2 release
|
||||||
- Bug fixes and improvements
|
- Bug fixes and improvements
|
||||||
|
|||||||
@@ -2,226 +2,121 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Build SRPM locally with correct tarball and upload to Copr
|
# Build SRPM locally with correct tarball and upload to Copr
|
||||||
# Usage: ./create-upload-copr.sh VERSION [RELEASE]
|
# Usage: ./copr-upload.sh [PACKAGE] [VERSION] [RELEASE]
|
||||||
# Example: ./create-upload-copr.sh 1.0.0 4
|
# 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)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && 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
|
# Setup build directories
|
||||||
mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
cd ~/rpmbuild/SOURCES
|
cd ~/rpmbuild/SOURCES
|
||||||
|
|
||||||
# Create the corrected QML tarball locally
|
# Download source tarball from GitHub releases
|
||||||
echo "Creating QML tarball with assets..."
|
echo "📦 Downloading source tarball for v${VERSION}..."
|
||||||
TEMP_DIR=$(mktemp -d)
|
if [ ! -f ~/rpmbuild/SOURCES/dms-qml.tar.gz ]; then
|
||||||
cd "$REPO_ROOT"
|
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}"
|
||||||
# Copy quickshell contents to temp
|
exit 1
|
||||||
cp -r quickshell/* "$TEMP_DIR/"
|
}
|
||||||
|
echo "✅ Source tarball downloaded"
|
||||||
# Copy root LICENSE and CONTRIBUTING.md
|
else
|
||||||
cp LICENSE CONTRIBUTING.md "$TEMP_DIR/"
|
echo "✅ Source tarball already exists"
|
||||||
|
|
||||||
# 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
|
|
||||||
fi
|
fi
|
||||||
# Signal running DMS instances to reload
|
|
||||||
pkill -USR1 -x dms >/dev/null 2>&1 || :
|
|
||||||
|
|
||||||
%files
|
# Copy and prepare spec file
|
||||||
%license LICENSE
|
echo "📝 Preparing spec file..."
|
||||||
%doc README.md CONTRIBUTING.md
|
SPEC_FILE="$REPO_ROOT/distro/fedora/${PACKAGE}.spec"
|
||||||
%{_datadir}/quickshell/dms/
|
if [ ! -f "$SPEC_FILE" ]; then
|
||||||
%{_userunitdir}/dms.service
|
echo "❌ Spec file not found: $SPEC_FILE"
|
||||||
%{_datadir}/applications/dms-open.desktop
|
exit 1
|
||||||
%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
fi
|
||||||
|
|
||||||
%files -n dms-cli
|
cp "$SPEC_FILE" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
||||||
%{_bindir}/dms
|
|
||||||
%{_datadir}/bash-completion/completions/dms
|
|
||||||
%{_datadir}/zsh/site-functions/_dms
|
|
||||||
%{_datadir}/fish/vendor_completions.d/dms.fish
|
|
||||||
|
|
||||||
%changelog
|
# Replace placeholders in spec file
|
||||||
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-1
|
CHANGELOG_DATE="$(date '+%a %b %d %Y')"
|
||||||
- Stable release VERSION_PLACEHOLDER
|
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
||||||
- Built locally with corrected tarball
|
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
||||||
SPECEOF
|
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
||||||
|
|
||||||
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/dms.spec
|
echo "✅ Spec file prepared for ${PACKAGE} v${VERSION}-${RELEASE}"
|
||||||
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/dms.spec
|
|
||||||
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/dms.spec
|
|
||||||
|
|
||||||
# Build SRPM
|
# Build SRPM
|
||||||
echo "Building SRPM..."
|
echo "🔨 Building SRPM..."
|
||||||
cd ~/rpmbuild/SPECS
|
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
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "SRPM built successfully: $SRPM"
|
echo "✅ SRPM built successfully: $SRPM"
|
||||||
|
|
||||||
# Check if copr-cli is installed
|
# Check if copr-cli is installed
|
||||||
if ! command -v copr-cli &>/dev/null; then
|
if ! command -v copr-cli &>/dev/null; then
|
||||||
echo ""
|
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 " pip install copr-cli"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Then configure it with your Copr API token in ~/.config/copr"
|
echo "Then configure it with your Copr API token in ~/.config/copr"
|
||||||
echo ""
|
echo ""
|
||||||
echo "SRPM is ready at: $SRPM"
|
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
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Upload to Copr
|
# Upload to Copr
|
||||||
echo ""
|
echo ""
|
||||||
echo "Uploading to Copr..."
|
echo "🚀 Uploading to Copr..."
|
||||||
if copr-cli build avengemedia/dms "$SRPM" --nowait; then
|
if copr-cli build "$COPR_PROJECT" "$SRPM" --nowait; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "Build submitted successfully! Check status at:"
|
echo "✅ Build submitted successfully!"
|
||||||
echo "https://copr.fedorainfracloud.org/coprs/avengemedia/dms/builds/"
|
echo "📊 Check status at:"
|
||||||
|
echo " https://copr.fedorainfracloud.org/coprs/${COPR_PROJECT}/builds/"
|
||||||
|
echo ""
|
||||||
|
echo "📦 SRPM location: $SRPM"
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
echo "Copr upload failed. You can manually upload the SRPM:"
|
echo "❌ Copr upload failed. You can manually upload the SRPM:"
|
||||||
echo " copr-cli build avengemedia/dms $SRPM"
|
echo " copr-cli build $COPR_PROJECT $SRPM"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Or upload via web interface:"
|
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 ""
|
||||||
echo "SRPM location: $SRPM"
|
echo "SRPM location: $SRPM"
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
# ./distro/scripts/obs-upload.sh dms "Update to v1.0.2"
|
# ./distro/scripts/obs-upload.sh dms "Update to v1.0.2"
|
||||||
# ./distro/scripts/obs-upload.sh debian dms
|
# ./distro/scripts/obs-upload.sh debian dms
|
||||||
# ./distro/scripts/obs-upload.sh opensuse dms-git
|
# ./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 debian dms-git 2 # Rebuild with db2 suffix
|
||||||
# ./distro/scripts/obs-upload.sh dms-git --rebuild=2 # Rebuild with ppa2 suffix (flag syntax)
|
# ./distro/scripts/obs-upload.sh dms-git --rebuild=2 # Rebuild with db2 suffix (flag syntax)
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -126,8 +126,8 @@ check_obs_version_exists() {
|
|||||||
OBS_VERSION=$(echo "$OBS_SPEC" | grep "^Version:" | awk '{print $2}' | xargs)
|
OBS_VERSION=$(echo "$OBS_SPEC" | grep "^Version:" | awk '{print $2}' | xargs)
|
||||||
# Commit hash check for -git packages
|
# Commit hash check for -git packages
|
||||||
if [[ "$CHECK_MODE" == "commit" ]] && [[ "$PACKAGE" == *"-git" ]]; then
|
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 "")
|
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})(ppa[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
|
if [[ -n "$OBS_COMMIT" && -n "$NEW_COMMIT" && "$OBS_COMMIT" == "$NEW_COMMIT" ]]; then
|
||||||
echo "⚠️ Commit $NEW_COMMIT already exists in OBS (current version: $OBS_VERSION)"
|
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)
|
# Apply rebuild suffix if specified (must happen before API check)
|
||||||
if [[ -n "$REBUILD_RELEASE" ]] && [[ -n "$CHANGELOG_VERSION" ]]; then
|
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"
|
echo " - Applied rebuild suffix: $CHANGELOG_VERSION"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -307,12 +308,16 @@ if [[ -d "distro/debian/$PACKAGE/debian" ]]; then
|
|||||||
else
|
else
|
||||||
# Rebuild number specified - check if this exact version already exists (exact mode)
|
# Rebuild number specified - check if this exact version already exists (exact mode)
|
||||||
if check_obs_version_exists "$OBS_PROJECT" "$PACKAGE" "$CHANGELOG_VERSION" "exact"; then
|
if check_obs_version_exists "$OBS_PROJECT" "$PACKAGE" "$CHANGELOG_VERSION" "exact"; then
|
||||||
echo "==> Error: Version $CHANGELOG_VERSION already exists in OBS"
|
echo "==> Version $CHANGELOG_VERSION already exists in OBS"
|
||||||
echo " This exact version (including ppa${REBUILD_RELEASE}) is already uploaded."
|
echo " This exact version (including db${REBUILD_RELEASE}) is already uploaded."
|
||||||
echo " To rebuild with a different release number, try incrementing:"
|
echo " Skipping upload - nothing to do."
|
||||||
|
echo ""
|
||||||
|
echo " 💡 To rebuild with a different release number, try incrementing:"
|
||||||
NEXT_NUM=$((REBUILD_RELEASE + 1))
|
NEXT_NUM=$((REBUILD_RELEASE + 1))
|
||||||
echo " ./distro/scripts/obs-upload.sh $PACKAGE $NEXT_NUM"
|
echo " REBUILD_RELEASE=$NEXT_NUM"
|
||||||
exit 1
|
echo ""
|
||||||
|
echo "✓ Exiting gracefully (no changes needed)"
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
fi
|
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
|
if [[ -n "$URL_PROTOCOL" && -n "$URL_HOST" && -n "$URL_PATH" ]]; then
|
||||||
SOURCE_URL="${URL_PROTOCOL}://${URL_HOST}${URL_PATH}"
|
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 ||
|
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
|
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
|
fi
|
||||||
SOURCE_DIR=$(cd "$SOURCE_DIR" && pwd)
|
SOURCE_DIR=$(cd "$SOURCE_DIR" && pwd)
|
||||||
cd "$REPO_ROOT"
|
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
|
else
|
||||||
echo "Error: Failed to download source from $SOURCE_URL"
|
echo "ERROR: Failed to download source from $SOURCE_URL"
|
||||||
echo "Tried both wget and curl. Please check the URL and network connectivity."
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -553,7 +566,7 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo " Found source directory: $SOURCE_DIR"
|
echo "==> Found source directory: $SOURCE_DIR"
|
||||||
|
|
||||||
# Vendor Go dependencies for dms-git
|
# Vendor Go dependencies for dms-git
|
||||||
if [[ "$PACKAGE" == "dms-git" ]] && [[ -d "$SOURCE_DIR/core" ]]; then
|
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")
|
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"
|
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/$COMBINED_TARBALL" "$TARBALL_BASE"
|
||||||
cd "$REPO_ROOT"
|
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
|
if [[ "$PACKAGE" == "dms" ]]; then
|
||||||
TARBALL_DIR=$(tar -tzf "$WORK_DIR/$COMBINED_TARBALL" 2>/dev/null | head -1 | cut -d'/' -f1)
|
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"
|
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"
|
tar --sort=name --mtime='2000-01-01 00:00:00' --owner=0 --group=0 -czf "$WORK_DIR/$COMBINED_TARBALL" "$TARBALL_BASE"
|
||||||
cd "$REPO_ROOT"
|
cd "$REPO_ROOT"
|
||||||
|
if [[ "$(pwd)" != "$REPO_ROOT" ]]; then
|
||||||
|
echo "ERROR: Failed to return to REPO_ROOT after tarball recreation"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -796,23 +817,29 @@ EOF
|
|||||||
fi
|
fi
|
||||||
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
|
# Server-side cleanup via API
|
||||||
echo "==> Cleaning old tarballs from OBS server (prevents downloading 100+ old versions)"
|
echo "==> Cleaning old tarballs from OBS server (prevents downloading 100+ old versions)"
|
||||||
OBS_FILES=$(osc api "/source/$OBS_PROJECT/$PACKAGE" 2>/dev/null || echo "")
|
OBS_FILES=$(osc api "/source/$OBS_PROJECT/$PACKAGE" 2>/dev/null || echo "")
|
||||||
if [[ -n "$OBS_FILES" ]]; then
|
if [[ -n "$OBS_FILES" ]]; then
|
||||||
DELETED_COUNT=0
|
DELETED_COUNT=0
|
||||||
KEEP_PATTERN=""
|
KEEP_CURRENT=""
|
||||||
if [[ -n "$CHANGELOG_VERSION" ]]; then
|
if [[ -n "$CHANGELOG_VERSION" ]]; then
|
||||||
BASE_KEEP_VERSION=$(echo "$CHANGELOG_VERSION" | sed 's/ppa[0-9]*$//')
|
KEEP_CURRENT="${PACKAGE}_${CHANGELOG_VERSION}.tar.gz"
|
||||||
KEEP_PATTERN="${PACKAGE}_${BASE_KEEP_VERSION}"
|
echo " Keeping only current version: ${KEEP_CURRENT}"
|
||||||
echo " Keeping tarballs matching: ${KEEP_PATTERN}*"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for old_file in $(echo "$OBS_FILES" | grep -oP '(?<=name=")[^"]*\.(tar\.gz|tar\.xz|tar\.bz2)(?=")' || true); do
|
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
|
if [[ "$old_file" == "$KEEP_CURRENT" ]]; then
|
||||||
echo " - Keeping current version: $old_file"
|
echo " - Keeping: $old_file"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -835,14 +862,11 @@ else
|
|||||||
echo " ⚠️ Could not fetch file list from server, skipping cleanup"
|
echo " ⚠️ Could not fetch file list from server, skipping cleanup"
|
||||||
fi
|
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"
|
echo "==> Updating working copy"
|
||||||
if ! osc up --server-side-source-service-files 2>/dev/null; then
|
if ! osc up 2>/dev/null; then
|
||||||
echo " Note: Using regular update (--server-side-source-service-files not supported)"
|
echo "Error: Failed to update working copy"
|
||||||
if ! osc up; then
|
exit 1
|
||||||
echo "Error: Failed to update working copy"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure we're in WORK_DIR and it exists
|
# Ensure we're in WORK_DIR and it exists
|
||||||
@@ -882,6 +906,15 @@ elif [[ "$UPLOAD_OPENSUSE" == true ]]; then
|
|||||||
fi
|
fi
|
||||||
echo ""
|
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
|
osc addremove 2>&1 | grep -v "Git SCM package" || true
|
||||||
|
|
||||||
SOURCE_TARBALL="${PACKAGE}-source.tar.gz"
|
SOURCE_TARBALL="${PACKAGE}-source.tar.gz"
|
||||||
@@ -908,7 +941,7 @@ if ! osc status 2>/dev/null | grep -qE '^[MAD]|^[?]'; then
|
|||||||
else
|
else
|
||||||
echo "==> Committing to OBS"
|
echo "==> Committing to OBS"
|
||||||
set +e
|
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]}
|
COMMIT_EXIT=${PIPESTATUS[0]}
|
||||||
set -e
|
set -e
|
||||||
if [[ $COMMIT_EXIT -ne 0 ]]; then
|
if [[ $COMMIT_EXIT -ne 0 ]]; then
|
||||||
|
|||||||
@@ -191,19 +191,8 @@ fi
|
|||||||
cd "$WORK_PACKAGE_DIR"
|
cd "$WORK_PACKAGE_DIR"
|
||||||
get_latest_tag() {
|
get_latest_tag() {
|
||||||
local repo="$1"
|
local repo="$1"
|
||||||
if command -v curl &>/dev/null; then
|
# Get the latest tag, sorted by version
|
||||||
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)
|
git ls-remote --tags --refs --sort='-v:refname' "https://github.com/$repo.git" | head -n1 | awk -F/ '{print $NF}' | sed 's/^v//'
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IS_GIT_PACKAGE=false
|
IS_GIT_PACKAGE=false
|
||||||
@@ -334,6 +323,17 @@ EOF
|
|||||||
fi
|
fi
|
||||||
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
|
if [ ! -f "dms-source.tar.gz" ]; then
|
||||||
info "Downloading dms source for QML files..."
|
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
|
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"
|
rm -f "$PACKAGE_DIR/dms-distropkg-amd64.gz"
|
||||||
REMOVED=$((REMOVED + 1))
|
REMOVED=$((REMOVED + 1))
|
||||||
fi
|
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
|
if [ -f "$PACKAGE_DIR/dms-source.tar.gz" ]; then
|
||||||
rm -f "$PACKAGE_DIR/dms-source.tar.gz"
|
rm -f "$PACKAGE_DIR/dms-source.tar.gz"
|
||||||
REMOVED=$((REMOVED + 1))
|
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
|
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||||
|
|
||||||
Package: dms-git
|
Package: dms-git
|
||||||
Architecture: amd64
|
Architecture: any
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
quickshell-git | quickshell,
|
quickshell-git | quickshell,
|
||||||
accountsservice,
|
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
|
sed -i 's/^go 1\.24\.[0-9]*/go 1.24/' dms-git-repo/core/go.mod
|
||||||
|
|
||||||
# Build dms-cli from source
|
# 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 \
|
@if [ -f dms-git-repo/.dms-version ]; then \
|
||||||
. dms-git-repo/.dms-version; \
|
. dms-git-repo/.dms-version; \
|
||||||
echo "Building with 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=amd64 VERSION="$$VERSION" COMMIT="$$COMMIT"; \
|
cd dms-git-repo/core && $(MAKE) GOFLAGS="-mod=vendor" dist ARCH=$(DEB_HOST_ARCH) VERSION="$$VERSION" COMMIT="$$COMMIT"; \
|
||||||
else \
|
else \
|
||||||
echo "Warning: .dms-version not found, building without version info"; \
|
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
|
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
|
chmod +x dms
|
||||||
|
|
||||||
override_dh_auto_install:
|
override_dh_auto_install:
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
|||||||
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||||
|
|
||||||
Package: dms-greeter
|
Package: dms-greeter
|
||||||
Architecture: all
|
Architecture: any
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
greetd,
|
greetd,
|
||||||
quickshell-git | quickshell
|
quickshell-git | quickshell
|
||||||
Recommends: niri | hyprland | sway
|
Recommends: niri | hyprland | sway
|
||||||
Description: DankMaterialShell greeter for greetd
|
Description: DankMaterialShell greeter for greetd
|
||||||
DankMaterialShell greeter for greetd login manager. A modern, Material Design 3
|
DankMaterialShell greeter for greetd login manager. A modern, Material Design 3
|
||||||
inspired greeter interface built with Quickshell for Wayland compositors.
|
inspired greeter interface built with Quickshell for Wayland compositors.
|
||||||
.
|
.
|
||||||
Supports multiple compositors including Niri, Hyprland, and Sway with automatic
|
Supports multiple compositors including Niri, Hyprland, and Sway with automatic
|
||||||
compositor detection and configuration. Features session selection, user
|
compositor detection and configuration. Features session selection, user
|
||||||
authentication, and dynamic theming.
|
authentication, and dynamic theming.
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ Vcs-Browser: https://github.com/AvengeMedia/DankMaterialShell
|
|||||||
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
Vcs-Git: https://github.com/AvengeMedia/DankMaterialShell.git
|
||||||
|
|
||||||
Package: dms
|
Package: dms
|
||||||
Architecture: amd64
|
Architecture: any
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
quickshell-git | quickshell,
|
quickshell | quickshell-git,
|
||||||
accountsservice,
|
accountsservice,
|
||||||
cava,
|
cava,
|
||||||
cliphist,
|
cliphist,
|
||||||
|
|||||||
@@ -19,11 +19,14 @@ override_dh_installsystemd:
|
|||||||
override_dh_auto_build:
|
override_dh_auto_build:
|
||||||
# All files are included in source package (downloaded by build-source.sh)
|
# All files are included in source package (downloaded by build-source.sh)
|
||||||
# Launchpad build environment has no internet access
|
# 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)
|
test -f dms-source.tar.gz || (echo "ERROR: dms-source.tar.gz not found!" && exit 1)
|
||||||
|
|
||||||
# Extract pre-built binary
|
# Extract pre-built binary
|
||||||
gunzip -c dms-distropkg-amd64.gz > dms
|
gunzip -c dms-distropkg-$(DEB_HOST_ARCH).gz > dms
|
||||||
chmod +x dms
|
chmod +x dms
|
||||||
|
|
||||||
# Extract source tarball for QML files
|
# 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
flake.lock
generated
22
flake.lock
generated
@@ -7,11 +7,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762835999,
|
"lastModified": 1765838956,
|
||||||
"narHash": "sha256-UykYGrGFOFTmDpKTLNxj1wvd1gbDG4TkqLNSbV0TYwk=",
|
"narHash": "sha256-A3a2ZfvjirX8VIdIPI+nAyukWs6vx4vet3fU0mpr7lU=",
|
||||||
"owner": "AvengeMedia",
|
"owner": "AvengeMedia",
|
||||||
"repo": "dgop",
|
"repo": "dgop",
|
||||||
"rev": "799301991cd5dcea9b64245f9d500dcc76615653",
|
"rev": "0ff697a4e3418966caa714c838fc73f1ef6ba59b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1764950072,
|
"lastModified": 1766651565,
|
||||||
"narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=",
|
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "f61125a668a320878494449750330ca58b78c557",
|
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -43,16 +43,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1764663772,
|
"lastModified": 1766386896,
|
||||||
"narHash": "sha256-sHqLmm0wAt3PC4vczJeBozI1/f4rv9yp3IjkClHDXDs=",
|
"narHash": "sha256-1uql4y229Rh+/2da99OVNe6DfsjObukXkf60TYRCvhI=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460",
|
"rev": "3918290c1bcd93ed81291844d9f1ed146672dbfc",
|
||||||
"revCount": 713,
|
"revCount": 714,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460",
|
"rev": "3918290c1bcd93ed81291844d9f1ed146672dbfc",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
||||||
}
|
}
|
||||||
|
|||||||
18
flake.nix
18
flake.nix
@@ -8,7 +8,7 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
quickshell = {
|
quickshell = {
|
||||||
url = "git+https://git.outfoxxed.me/quickshell/quickshell?rev=26531fc46ef17e9365b03770edd3fb9206fcb460";
|
url = "git+https://git.outfoxxed.me/quickshell/quickshell?rev=3918290c1bcd93ed81291844d9f1ed146672dbfc";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -149,14 +149,24 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
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.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 (
|
devShells = forEachSystem (
|
||||||
system: pkgs:
|
system: pkgs:
|
||||||
let
|
let
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ Singleton {
|
|||||||
return [fullUnderscore, fullHyphen, _lang].filter(c => c && c !== "en");
|
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")
|
readonly property url translationsFolder: Qt.resolvedUrl("../translations/poexports")
|
||||||
|
|
||||||
property string currentLocale: "en"
|
property string currentLocale: "en"
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ Singleton {
|
|||||||
id: modalManager
|
id: modalManager
|
||||||
|
|
||||||
signal closeAllModalsExcept(var excludedModal)
|
signal closeAllModalsExcept(var excludedModal)
|
||||||
|
signal modalChanged
|
||||||
|
|
||||||
|
property var currentModalsByScreen: ({})
|
||||||
|
|
||||||
function openModal(modal) {
|
function openModal(modal) {
|
||||||
if (!modal.allowStacking) {
|
if (!modal.allowStacking) {
|
||||||
@@ -17,5 +20,17 @@ Singleton {
|
|||||||
PopoutManager.closeAllPopouts();
|
PopoutManager.closeAllPopouts();
|
||||||
}
|
}
|
||||||
TrayMenuManager.closeAllMenus();
|
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 Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior
|
||||||
|
|
||||||
import QtCore
|
import QtCore
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -14,7 +14,7 @@ import "settings/SettingsStore.js" as Store
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
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"
|
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 alias dankBarRightWidgetsModel: rightWidgetsModel
|
||||||
|
|
||||||
property string currentThemeName: "blue"
|
property string currentThemeName: "blue"
|
||||||
|
property string currentThemeCategory: "generic"
|
||||||
property string customThemeFile: ""
|
property string customThemeFile: ""
|
||||||
|
property var registryThemeVariants: ({})
|
||||||
property string matugenScheme: "scheme-tonal-spot"
|
property string matugenScheme: "scheme-tonal-spot"
|
||||||
property bool runUserMatugenTemplates: true
|
property bool runUserMatugenTemplates: true
|
||||||
property string matugenTargetMonitor: ""
|
property string matugenTargetMonitor: ""
|
||||||
@@ -72,6 +74,8 @@ Singleton {
|
|||||||
property string widgetBackgroundColor: "sch"
|
property string widgetBackgroundColor: "sch"
|
||||||
property string widgetColorMode: "default"
|
property string widgetColorMode: "default"
|
||||||
property real cornerRadius: 12
|
property real cornerRadius: 12
|
||||||
|
property int niriLayoutGapsOverride: -1
|
||||||
|
property int niriLayoutRadiusOverride: -1
|
||||||
|
|
||||||
property bool use24HourClock: true
|
property bool use24HourClock: true
|
||||||
property bool showSeconds: false
|
property bool showSeconds: false
|
||||||
@@ -105,9 +109,12 @@ Singleton {
|
|||||||
property bool controlCenterShowNetworkIcon: true
|
property bool controlCenterShowNetworkIcon: true
|
||||||
property bool controlCenterShowBluetoothIcon: true
|
property bool controlCenterShowBluetoothIcon: true
|
||||||
property bool controlCenterShowAudioIcon: true
|
property bool controlCenterShowAudioIcon: true
|
||||||
|
property bool controlCenterShowAudioPercent: true
|
||||||
property bool controlCenterShowVpnIcon: true
|
property bool controlCenterShowVpnIcon: true
|
||||||
property bool controlCenterShowBrightnessIcon: false
|
property bool controlCenterShowBrightnessIcon: false
|
||||||
|
property bool controlCenterShowBrightnessPercent: false
|
||||||
property bool controlCenterShowMicIcon: false
|
property bool controlCenterShowMicIcon: false
|
||||||
|
property bool controlCenterShowMicPercent: true
|
||||||
property bool controlCenterShowBatteryIcon: false
|
property bool controlCenterShowBatteryIcon: false
|
||||||
property bool controlCenterShowPrinterIcon: false
|
property bool controlCenterShowPrinterIcon: false
|
||||||
property bool showPrivacyButton: true
|
property bool showPrivacyButton: true
|
||||||
@@ -170,6 +177,7 @@ Singleton {
|
|||||||
property bool waveProgressEnabled: true
|
property bool waveProgressEnabled: true
|
||||||
property bool scrollTitleEnabled: true
|
property bool scrollTitleEnabled: true
|
||||||
property bool audioVisualizerEnabled: true
|
property bool audioVisualizerEnabled: true
|
||||||
|
property bool audioScrollEnabled: true
|
||||||
property bool clockCompactMode: false
|
property bool clockCompactMode: false
|
||||||
property bool focusedWindowCompactMode: false
|
property bool focusedWindowCompactMode: false
|
||||||
property bool runningAppsCompactMode: true
|
property bool runningAppsCompactMode: true
|
||||||
@@ -255,6 +263,7 @@ Singleton {
|
|||||||
property int batterySuspendTimeout: 0
|
property int batterySuspendTimeout: 0
|
||||||
property int batterySuspendBehavior: SettingsData.SuspendBehavior.Suspend
|
property int batterySuspendBehavior: SettingsData.SuspendBehavior.Suspend
|
||||||
property string batteryProfileName: ""
|
property string batteryProfileName: ""
|
||||||
|
property int batteryChargeLimit: 100
|
||||||
property bool lockBeforeSuspend: false
|
property bool lockBeforeSuspend: false
|
||||||
property bool loginctlLockIntegration: true
|
property bool loginctlLockIntegration: true
|
||||||
property bool fadeToLockEnabled: false
|
property bool fadeToLockEnabled: false
|
||||||
@@ -279,9 +288,11 @@ Singleton {
|
|||||||
property bool matugenTemplateFirefox: true
|
property bool matugenTemplateFirefox: true
|
||||||
property bool matugenTemplatePywalfox: true
|
property bool matugenTemplatePywalfox: true
|
||||||
property bool matugenTemplateVesktop: true
|
property bool matugenTemplateVesktop: true
|
||||||
|
property bool matugenTemplateEquibop: true
|
||||||
property bool matugenTemplateGhostty: true
|
property bool matugenTemplateGhostty: true
|
||||||
property bool matugenTemplateKitty: true
|
property bool matugenTemplateKitty: true
|
||||||
property bool matugenTemplateFoot: true
|
property bool matugenTemplateFoot: true
|
||||||
|
property bool matugenTemplateNeovim: true
|
||||||
property bool matugenTemplateAlacritty: true
|
property bool matugenTemplateAlacritty: true
|
||||||
property bool matugenTemplateWezterm: true
|
property bool matugenTemplateWezterm: true
|
||||||
property bool matugenTemplateDgop: true
|
property bool matugenTemplateDgop: true
|
||||||
@@ -302,6 +313,7 @@ Singleton {
|
|||||||
property string dockBorderColor: "surfaceText"
|
property string dockBorderColor: "surfaceText"
|
||||||
property real dockBorderOpacity: 1.0
|
property real dockBorderOpacity: 1.0
|
||||||
property int dockBorderThickness: 1
|
property int dockBorderThickness: 1
|
||||||
|
property bool dockIsolateDisplays: false
|
||||||
|
|
||||||
property bool notificationOverlayEnabled: false
|
property bool notificationOverlayEnabled: false
|
||||||
property int overviewRows: 2
|
property int overviewRows: 2
|
||||||
@@ -360,50 +372,259 @@ Singleton {
|
|||||||
property string displayNameMode: "system"
|
property string displayNameMode: "system"
|
||||||
property var screenPreferences: ({})
|
property var screenPreferences: ({})
|
||||||
property var showOnLastDisplay: ({})
|
property var showOnLastDisplay: ({})
|
||||||
|
property var niriOutputSettings: ({})
|
||||||
|
property var hyprlandOutputSettings: ({})
|
||||||
|
|
||||||
property var barConfigs: [
|
property var barConfigs: [
|
||||||
{
|
{
|
||||||
id: "default",
|
"id": "default",
|
||||||
name: "Main Bar",
|
"name": "Main Bar",
|
||||||
enabled: true,
|
"enabled": true,
|
||||||
position: 0,
|
"position": 0,
|
||||||
screenPreferences: ["all"],
|
"screenPreferences": ["all"],
|
||||||
showOnLastDisplay: true,
|
"showOnLastDisplay": true,
|
||||||
leftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"],
|
"leftWidgets": ["launcherButton", "workspaceSwitcher", "focusedWindow"],
|
||||||
centerWidgets: ["music", "clock", "weather"],
|
"centerWidgets": ["music", "clock", "weather"],
|
||||||
rightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
|
"rightWidgets": ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
|
||||||
spacing: 4,
|
"spacing": 4,
|
||||||
innerPadding: 4,
|
"innerPadding": 4,
|
||||||
bottomGap: 0,
|
"bottomGap": 0,
|
||||||
transparency: 1.0,
|
"transparency": 1.0,
|
||||||
widgetTransparency: 1.0,
|
"widgetTransparency": 1.0,
|
||||||
squareCorners: false,
|
"squareCorners": false,
|
||||||
noBackground: false,
|
"noBackground": false,
|
||||||
gothCornersEnabled: false,
|
"gothCornersEnabled": false,
|
||||||
gothCornerRadiusOverride: false,
|
"gothCornerRadiusOverride": false,
|
||||||
gothCornerRadiusValue: 12,
|
"gothCornerRadiusValue": 12,
|
||||||
borderEnabled: false,
|
"borderEnabled": false,
|
||||||
borderColor: "surfaceText",
|
"borderColor": "surfaceText",
|
||||||
borderOpacity: 1.0,
|
"borderOpacity": 1.0,
|
||||||
borderThickness: 1,
|
"borderThickness": 1,
|
||||||
widgetOutlineEnabled: false,
|
"widgetOutlineEnabled": false,
|
||||||
widgetOutlineColor: "primary",
|
"widgetOutlineColor": "primary",
|
||||||
widgetOutlineOpacity: 1.0,
|
"widgetOutlineOpacity": 1.0,
|
||||||
widgetOutlineThickness: 1,
|
"widgetOutlineThickness": 1,
|
||||||
fontScale: 1.0,
|
"fontScale": 1.0,
|
||||||
autoHide: false,
|
"autoHide": false,
|
||||||
autoHideDelay: 250,
|
"autoHideDelay": 250,
|
||||||
openOnOverview: false,
|
"showOnWindowsOpen": false,
|
||||||
visible: true,
|
"openOnOverview": false,
|
||||||
popupGapsAuto: true,
|
"visible": true,
|
||||||
popupGapsManual: 4,
|
"popupGapsAuto": true,
|
||||||
maximizeDetection: true,
|
"popupGapsManual": 4,
|
||||||
scrollEnabled: true,
|
"maximizeDetection": true,
|
||||||
scrollXBehavior: "column",
|
"scrollEnabled": true,
|
||||||
scrollYBehavior: "workspace"
|
"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 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 forceDankBarLayoutRefresh
|
||||||
signal forceDockLayoutRefresh
|
signal forceDockLayoutRefresh
|
||||||
signal widgetDataChanged
|
signal widgetDataChanged
|
||||||
@@ -421,10 +642,12 @@ Singleton {
|
|||||||
|
|
||||||
function applyStoredTheme() {
|
function applyStoredTheme() {
|
||||||
if (typeof Theme !== "undefined") {
|
if (typeof Theme !== "undefined") {
|
||||||
|
Theme.currentThemeCategory = currentThemeCategory;
|
||||||
Theme.switchTheme(currentThemeName, false, false);
|
Theme.switchTheme(currentThemeName, false, false);
|
||||||
} else {
|
} else {
|
||||||
Qt.callLater(function () {
|
Qt.callLater(function () {
|
||||||
if (typeof Theme !== "undefined") {
|
if (typeof Theme !== "undefined") {
|
||||||
|
Theme.currentThemeCategory = currentThemeCategory;
|
||||||
Theme.switchTheme(currentThemeName, false, false);
|
Theme.switchTheme(currentThemeName, false, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -458,25 +681,25 @@ Singleton {
|
|||||||
|
|
||||||
const configScript = `mkdir -p ${_configDir}/gtk-3.0 ${_configDir}/gtk-4.0
|
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
|
for config_dir in ${_configDir}/gtk-3.0 ${_configDir}/gtk-4.0; do
|
||||||
settings_file="$config_dir/settings.ini"
|
settings_file="$config_dir/settings.ini"
|
||||||
if [ -f "$settings_file" ]; then
|
if [ -f "$settings_file" ]; then
|
||||||
if grep -q "^gtk-icon-theme-name=" "$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
|
else
|
||||||
if grep -q "\\[Settings\\]" "$settings_file"; then
|
if grep -q "\\[Settings\\]" "$settings_file"; then
|
||||||
sed -i '/\\[Settings\\]/a gtk-icon-theme-name=${gtkThemeName}' "$settings_file"
|
sed -i '/\\[Settings\\]/a gtk-icon-theme-name=${gtkThemeName}' "$settings_file"
|
||||||
else
|
else
|
||||||
echo -e '\\n[Settings]\\ngtk-icon-theme-name=${gtkThemeName}' >> "$settings_file"
|
echo -e '\\n[Settings]\\ngtk-icon-theme-name=${gtkThemeName}' >> "$settings_file"
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
else
|
fi
|
||||||
|
else
|
||||||
echo -e '[Settings]\\ngtk-icon-theme-name=${gtkThemeName}' > "$settings_file"
|
echo -e '[Settings]\\ngtk-icon-theme-name=${gtkThemeName}' > "$settings_file"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
rm -rf ~/.cache/icon-cache ~/.cache/thumbnails 2>/dev/null || true
|
rm -rf ~/.cache/icon-cache ~/.cache/thumbnails 2>/dev/null || true
|
||||||
pkill -HUP -f 'gtk' 2>/dev/null || true`;
|
pkill -HUP -f 'gtk' 2>/dev/null || true`;
|
||||||
|
|
||||||
Quickshell.execDetached(["sh", "-lc", configScript]);
|
Quickshell.execDetached(["sh", "-lc", configScript]);
|
||||||
}
|
}
|
||||||
@@ -489,36 +712,36 @@ pkill -HUP -f 'gtk' 2>/dev/null || true`;
|
|||||||
const qtThemeNameEscaped = qtThemeName.replace(/'/g, "'\\''");
|
const qtThemeNameEscaped = qtThemeName.replace(/'/g, "'\\''");
|
||||||
|
|
||||||
const script = `mkdir -p ${_configDir}/qt5ct ${_configDir}/qt6ct ${_configDir}/environment.d 2>/dev/null || true
|
const script = `mkdir -p ${_configDir}/qt5ct ${_configDir}/qt6ct ${_configDir}/environment.d 2>/dev/null || true
|
||||||
update_qt_icon_theme() {
|
update_qt_icon_theme() {
|
||||||
local config_file="$1"
|
local config_file="$1"
|
||||||
local theme_name="$2"
|
local theme_name="$2"
|
||||||
if [ -f "$config_file" ]; then
|
if [ -f "$config_file" ]; then
|
||||||
if grep -q "^\\[Appearance\\]" "$config_file"; then
|
if grep -q "^\\[Appearance\\]" "$config_file"; then
|
||||||
if grep -q "^icon_theme=" "$config_file"; then
|
if grep -q "^icon_theme=" "$config_file"; then
|
||||||
sed -i "s/^icon_theme=.*/icon_theme=$theme_name/" "$config_file"
|
sed -i "s/^icon_theme=.*/icon_theme=$theme_name/" "$config_file"
|
||||||
else
|
else
|
||||||
sed -i "/^\\[Appearance\\]/a icon_theme=$theme_name" "$config_file"
|
sed -i "/^\\[Appearance\\]/a icon_theme=$theme_name" "$config_file"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
printf "\\n[Appearance]\\nicon_theme=%s\\n" "$theme_name" >> "$config_file"
|
printf "\\n[Appearance]\\nicon_theme=%s\\n" "$theme_name" >> "$config_file"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
printf "[Appearance]\\nicon_theme=%s\\n" "$theme_name" > "$config_file"
|
printf "[Appearance]\\nicon_theme=%s\\n" "$theme_name" > "$config_file"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
update_qt_icon_theme ${_configDir}/qt5ct/qt5ct.conf '${qtThemeNameEscaped}'
|
update_qt_icon_theme ${_configDir}/qt5ct/qt5ct.conf '${qtThemeNameEscaped}'
|
||||||
update_qt_icon_theme ${_configDir}/qt6ct/qt6ct.conf '${qtThemeNameEscaped}'
|
update_qt_icon_theme ${_configDir}/qt6ct/qt6ct.conf '${qtThemeNameEscaped}'
|
||||||
rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || true`;
|
rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || true`;
|
||||||
|
|
||||||
Quickshell.execDetached(["sh", "-lc", script]);
|
Quickshell.execDetached(["sh", "-lc", script]);
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property var _hooks: ({
|
readonly property var _hooks: ({
|
||||||
applyStoredTheme: applyStoredTheme,
|
"applyStoredTheme": applyStoredTheme,
|
||||||
regenSystemThemes: regenSystemThemes,
|
"regenSystemThemes": regenSystemThemes,
|
||||||
updateNiriLayout: updateNiriLayout,
|
"updateNiriLayout": updateNiriLayout,
|
||||||
applyStoredIconTheme: applyStoredIconTheme,
|
"applyStoredIconTheme": applyStoredIconTheme,
|
||||||
updateBarConfigs: updateBarConfigs
|
"updateBarConfigs": updateBarConfigs
|
||||||
})
|
})
|
||||||
|
|
||||||
function set(key, value) {
|
function set(key, value) {
|
||||||
@@ -543,7 +766,6 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
Store.parse(root, obj);
|
Store.parse(root, obj);
|
||||||
applyStoredTheme();
|
applyStoredTheme();
|
||||||
applyStoredIconTheme();
|
applyStoredIconTheme();
|
||||||
Processes.detectIcons();
|
|
||||||
Processes.detectQtTools();
|
Processes.detectQtTools();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("SettingsData: Failed to load settings:", e.message);
|
console.warn("SettingsData: Failed to load settings:", e.message);
|
||||||
@@ -590,7 +812,42 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
}
|
}
|
||||||
|
|
||||||
function detectAvailableIconThemes() {
|
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() {
|
function getEffectiveTimeFormat() {
|
||||||
@@ -654,15 +911,14 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
return barHeight + spacing + bottomGap - gothOffset + Theme.popupDistance;
|
return barHeight + spacing + bottomGap - gothOffset + Theme.popupDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPopupTriggerPosition(globalPos, screen, barThickness, widgetWidth, barSpacing, barPosition, barConfig) {
|
function getPopupTriggerPosition(pos, screen, barThickness, widgetWidth, barSpacing, barPosition, barConfig) {
|
||||||
const screenX = screen ? screen.x : 0;
|
const relativeX = pos.x;
|
||||||
const screenY = screen ? screen.y : 0;
|
const relativeY = pos.y;
|
||||||
const relativeX = globalPos.x - screenX;
|
|
||||||
const relativeY = globalPos.y - screenY;
|
|
||||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||||
const spacing = barSpacing !== undefined ? barSpacing : (defaultBar?.spacing ?? 4);
|
const spacing = barSpacing !== undefined ? barSpacing : (defaultBar?.spacing ?? 4);
|
||||||
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
|
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 useAutoGaps = (barConfig && barConfig.popupGapsAuto !== undefined) ? barConfig.popupGapsAuto : (defaultBar?.popupGapsAuto ?? true);
|
||||||
const manualGapValue = (barConfig && barConfig.popupGapsManual !== undefined) ? barConfig.popupGapsManual : (defaultBar?.popupGapsManual ?? 4);
|
const manualGapValue = (barConfig && barConfig.popupGapsManual !== undefined) ? barConfig.popupGapsManual : (defaultBar?.popupGapsManual ?? 4);
|
||||||
@@ -723,7 +979,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
let leftBar = 0;
|
let leftBar = 0;
|
||||||
let rightBar = 0;
|
let rightBar = 0;
|
||||||
|
|
||||||
for (let i = 0; i < enabledBars.length; i++) {
|
for (var i = 0; i < enabledBars.length; i++) {
|
||||||
const other = enabledBars[i];
|
const other = enabledBars[i];
|
||||||
if (other.id === barConfig.id)
|
if (other.id === barConfig.id)
|
||||||
continue;
|
continue;
|
||||||
@@ -793,7 +1049,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
|
|
||||||
if (barConfig) {
|
if (barConfig) {
|
||||||
const enabledBars = getEnabledBarConfigs();
|
const enabledBars = getEnabledBarConfigs();
|
||||||
for (let i = 0; i < enabledBars.length; i++) {
|
for (var i = 0; i < enabledBars.length; i++) {
|
||||||
const other = enabledBars[i];
|
const other = enabledBars[i];
|
||||||
if (other.id === barConfig.id)
|
if (other.id === barConfig.id)
|
||||||
continue;
|
continue;
|
||||||
@@ -913,7 +1169,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
updateBarConfigs();
|
updateBarConfigs();
|
||||||
|
|
||||||
if (positionChanged) {
|
if (positionChanged) {
|
||||||
NotificationService.clearAllPopups();
|
NotificationService.dismissAllPopups();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,7 +1181,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const conflicts = [];
|
const conflicts = [];
|
||||||
const enabledBars = getEnabledBarConfigs();
|
const enabledBars = getEnabledBarConfigs();
|
||||||
|
|
||||||
for (let i = 0; i < enabledBars.length; i++) {
|
for (var i = 0; i < enabledBars.length; i++) {
|
||||||
const other = enabledBars[i];
|
const other = enabledBars[i];
|
||||||
if (other.id === barId)
|
if (other.id === barId)
|
||||||
continue;
|
continue;
|
||||||
@@ -938,9 +1194,9 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const hasAll = barScreens.includes("all") || otherScreens.includes("all");
|
const hasAll = barScreens.includes("all") || otherScreens.includes("all");
|
||||||
if (hasAll) {
|
if (hasAll) {
|
||||||
conflicts.push({
|
conflicts.push({
|
||||||
barId: other.id,
|
"barId": other.id,
|
||||||
barName: other.name,
|
"barName": other.name,
|
||||||
reason: "Same position on all screens"
|
"reason": "Same position on all screens"
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -948,9 +1204,9 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const overlapping = barScreens.some(screen => otherScreens.includes(screen));
|
const overlapping = barScreens.some(screen => otherScreens.includes(screen));
|
||||||
if (overlapping) {
|
if (overlapping) {
|
||||||
conflicts.push({
|
conflicts.push({
|
||||||
barId: other.id,
|
"barId": other.id,
|
||||||
barName: other.name,
|
"barName": other.name,
|
||||||
reason: "Same position on overlapping screens"
|
"reason": "Same position on overlapping screens"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -972,7 +1228,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
|
|
||||||
function getScreensSortedByPosition() {
|
function getScreensSortedByPosition() {
|
||||||
const screens = [];
|
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.push(Quickshell.screens[i]);
|
||||||
}
|
}
|
||||||
screens.sort((a, b) => {
|
screens.sort((a, b) => {
|
||||||
@@ -989,7 +1245,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const sorted = getScreensSortedByPosition();
|
const sorted = getScreensSortedByPosition();
|
||||||
let modelCount = 0;
|
let modelCount = 0;
|
||||||
let screenIndex = -1;
|
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].model === screen.model) {
|
||||||
if (sorted[i].name === screen.name) {
|
if (sorted[i].name === screen.name) {
|
||||||
screenIndex = modelCount;
|
screenIndex = modelCount;
|
||||||
@@ -1058,7 +1314,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sendTestNotifications() {
|
function sendTestNotifications() {
|
||||||
NotificationService.clearAllPopups();
|
NotificationService.dismissAllPopups();
|
||||||
sendTestNotification(0);
|
sendTestNotification(0);
|
||||||
testNotifTimer1.start();
|
testNotifTimer1.start();
|
||||||
testNotifTimer2.start();
|
testNotifTimer2.start();
|
||||||
@@ -1187,7 +1443,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||||
if (defaultBar) {
|
if (defaultBar) {
|
||||||
updateBarConfig(defaultBar.id, {
|
updateBarConfig(defaultBar.id, {
|
||||||
spacing: spacing
|
"spacing": spacing
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
|
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
|
||||||
@@ -1216,7 +1472,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateBarConfig(defaultBar.id, {
|
updateBarConfig(defaultBar.id, {
|
||||||
position: position
|
"position": position
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1224,7 +1480,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||||
if (defaultBar) {
|
if (defaultBar) {
|
||||||
updateBarConfig(defaultBar.id, {
|
updateBarConfig(defaultBar.id, {
|
||||||
leftWidgets: order
|
"leftWidgets": order
|
||||||
});
|
});
|
||||||
updateListModel(leftWidgetsModel, order);
|
updateListModel(leftWidgetsModel, order);
|
||||||
}
|
}
|
||||||
@@ -1234,7 +1490,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||||
if (defaultBar) {
|
if (defaultBar) {
|
||||||
updateBarConfig(defaultBar.id, {
|
updateBarConfig(defaultBar.id, {
|
||||||
centerWidgets: order
|
"centerWidgets": order
|
||||||
});
|
});
|
||||||
updateListModel(centerWidgetsModel, order);
|
updateListModel(centerWidgetsModel, order);
|
||||||
}
|
}
|
||||||
@@ -1244,7 +1500,7 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||||
if (defaultBar) {
|
if (defaultBar) {
|
||||||
updateBarConfig(defaultBar.id, {
|
updateBarConfig(defaultBar.id, {
|
||||||
rightWidgets: order
|
"rightWidgets": order
|
||||||
});
|
});
|
||||||
updateListModel(rightWidgetsModel, order);
|
updateListModel(rightWidgetsModel, order);
|
||||||
}
|
}
|
||||||
@@ -1257,9 +1513,9 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||||
if (defaultBar) {
|
if (defaultBar) {
|
||||||
updateBarConfig(defaultBar.id, {
|
updateBarConfig(defaultBar.id, {
|
||||||
leftWidgets: defaultLeft,
|
"leftWidgets": defaultLeft,
|
||||||
centerWidgets: defaultCenter,
|
"centerWidgets": defaultCenter,
|
||||||
rightWidgets: defaultRight
|
"rightWidgets": defaultRight
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateListModel(leftWidgetsModel, defaultLeft);
|
updateListModel(leftWidgetsModel, defaultLeft);
|
||||||
@@ -1303,11 +1559,46 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
return workspaceNameIcons[workspaceName] || null;
|
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() {
|
function toggleDankBarVisible() {
|
||||||
const defaultBar = barConfigs[0] || getBarConfig("default");
|
const defaultBar = barConfigs[0] || getBarConfig("default");
|
||||||
if (defaultBar) {
|
if (defaultBar) {
|
||||||
updateBarConfig(defaultBar.id, {
|
updateBarConfig(defaultBar.id, {
|
||||||
visible: !defaultBar.visible
|
"visible": !defaultBar.visible
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1345,6 +1636,87 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
return settings ? JSON.parse(JSON.stringify(settings)) : {};
|
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 {
|
ListModel {
|
||||||
id: leftWidgetsModel
|
id: leftWidgetsModel
|
||||||
}
|
}
|
||||||
@@ -1396,6 +1768,8 @@ rm -rf '${home}'/.cache/icon-cache '${home}'/.cache/thumbnails 2>/dev/null || tr
|
|||||||
const txt = settingsFile.text();
|
const txt = settingsFile.text();
|
||||||
const obj = (txt && txt.trim()) ? JSON.parse(txt) : null;
|
const obj = (txt && txt.trim()) ? JSON.parse(txt) : null;
|
||||||
Store.parse(root, obj);
|
Store.parse(root, obj);
|
||||||
|
applyStoredTheme();
|
||||||
|
applyStoredIconTheme();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("SettingsData: Failed to reload settings:", e.message);
|
console.warn("SettingsData: Failed to reload settings:", e.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,545 +1,430 @@
|
|||||||
// Stock theme definitions for DankMaterialShell
|
// Stock theme definitions for DankMaterialShell
|
||||||
// Separated from Theme.qml to keep that file clean
|
// 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 = {
|
const StockThemes = {
|
||||||
DARK: {
|
DARK: {
|
||||||
blue: {
|
blue: {
|
||||||
name: "Blue",
|
name: "Blue",
|
||||||
primary: "#42a5f5",
|
primary: "#42a5f5",
|
||||||
primaryText: "#000000",
|
primaryText: "#000000",
|
||||||
primaryContainer: "#0d47a1",
|
primaryContainer: "#0d47a1",
|
||||||
secondary: "#8ab4f8",
|
secondary: "#8ab4f8",
|
||||||
surface: "#101418",
|
surface: "#101418",
|
||||||
surfaceText: "#e0e2e8",
|
surfaceText: "#e0e2e8",
|
||||||
surfaceVariant: "#42474e",
|
surfaceVariant: "#42474e",
|
||||||
surfaceVariantText: "#c2c7cf",
|
surfaceVariantText: "#c2c7cf",
|
||||||
surfaceTint: "#8ab4f8",
|
surfaceTint: "#8ab4f8",
|
||||||
background: "#101418",
|
background: "#101418",
|
||||||
backgroundText: "#e0e2e8",
|
backgroundText: "#e0e2e8",
|
||||||
outline: "#8c9199",
|
outline: "#8c9199",
|
||||||
surfaceContainer: "#1d2024",
|
surfaceContainer: "#1d2024",
|
||||||
surfaceContainerHigh: "#272a2f",
|
surfaceContainerHigh: "#272a2f",
|
||||||
surfaceContainerHighest: "#32353a"
|
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"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
LIGHT: {
|
purple: {
|
||||||
blue: {
|
name: "Purple",
|
||||||
name: "Blue Light",
|
primary: "#D0BCFF",
|
||||||
primary: "#1976d2",
|
primaryText: "#381E72",
|
||||||
primaryText: "#ffffff",
|
primaryContainer: "#4F378B",
|
||||||
primaryContainer: "#e3f2fd",
|
secondary: "#CCC2DC",
|
||||||
secondary: "#42a5f5",
|
surface: "#141218",
|
||||||
surface: "#f7f9ff",
|
surfaceText: "#e6e0e9",
|
||||||
surfaceText: "#181c20",
|
surfaceVariant: "#49454e",
|
||||||
surfaceVariant: "#dee3eb",
|
surfaceVariantText: "#cac4cf",
|
||||||
surfaceVariantText: "#42474e",
|
surfaceTint: "#D0BCFF",
|
||||||
surfaceTint: "#1976d2",
|
background: "#141218",
|
||||||
background: "#f7f9ff",
|
backgroundText: "#e6e0e9",
|
||||||
backgroundText: "#181c20",
|
outline: "#948f99",
|
||||||
outline: "#72777f",
|
surfaceContainer: "#211f24",
|
||||||
surfaceContainer: "#eceef4",
|
surfaceContainerHigh: "#2b292f",
|
||||||
surfaceContainerHigh: "#e6e8ee",
|
surfaceContainerHighest: "#36343a",
|
||||||
surfaceContainerHighest: "#e0e2e8"
|
},
|
||||||
},
|
green: {
|
||||||
purple: {
|
name: "Green",
|
||||||
name: "Purple Light",
|
primary: "#4caf50",
|
||||||
primary: "#6750A4",
|
primaryText: "#000000",
|
||||||
primaryText: "#ffffff",
|
primaryContainer: "#1b5e20",
|
||||||
primaryContainer: "#EADDFF",
|
secondary: "#81c995",
|
||||||
secondary: "#625B71",
|
surface: "#10140f",
|
||||||
surface: "#fef7ff",
|
surfaceText: "#e0e4db",
|
||||||
surfaceText: "#1d1b20",
|
surfaceVariant: "#424940",
|
||||||
surfaceVariant: "#e7e0eb",
|
surfaceVariantText: "#c2c9bd",
|
||||||
surfaceVariantText: "#49454e",
|
surfaceTint: "#81c995",
|
||||||
surfaceTint: "#6750A4",
|
background: "#10140f",
|
||||||
background: "#fef7ff",
|
backgroundText: "#e0e4db",
|
||||||
backgroundText: "#1d1b20",
|
outline: "#8c9388",
|
||||||
outline: "#7a757f",
|
surfaceContainer: "#1d211b",
|
||||||
surfaceContainer: "#f2ecf4",
|
surfaceContainerHigh: "#272b25",
|
||||||
surfaceContainerHigh: "#ece6ee",
|
surfaceContainerHighest: "#323630",
|
||||||
surfaceContainerHighest: "#e6e0e9"
|
},
|
||||||
},
|
orange: {
|
||||||
green: {
|
name: "Orange",
|
||||||
name: "Green Light",
|
primary: "#ff6d00",
|
||||||
primary: "#2e7d32",
|
primaryText: "#000000",
|
||||||
primaryText: "#ffffff",
|
primaryContainer: "#3e2723",
|
||||||
primaryContainer: "#e8f5e8",
|
secondary: "#ffb74d",
|
||||||
secondary: "#4caf50",
|
surface: "#1a120e",
|
||||||
surface: "#f7fbf1",
|
surfaceText: "#f0dfd8",
|
||||||
surfaceText: "#191d17",
|
surfaceVariant: "#52443d",
|
||||||
surfaceVariant: "#dee5d8",
|
surfaceVariantText: "#d7c2b9",
|
||||||
surfaceVariantText: "#424940",
|
surfaceTint: "#ffb74d",
|
||||||
surfaceTint: "#2e7d32",
|
background: "#1a120e",
|
||||||
background: "#f7fbf1",
|
backgroundText: "#f0dfd8",
|
||||||
backgroundText: "#191d17",
|
outline: "#a08d85",
|
||||||
outline: "#72796f",
|
surfaceContainer: "#271e1a",
|
||||||
surfaceContainer: "#ecefe6",
|
surfaceContainerHigh: "#322824",
|
||||||
surfaceContainerHigh: "#e6e9e0",
|
surfaceContainerHighest: "#3d332e",
|
||||||
surfaceContainerHighest: "#e0e4db"
|
},
|
||||||
},
|
red: {
|
||||||
orange: {
|
name: "Red",
|
||||||
name: "Orange Light",
|
primary: "#f44336",
|
||||||
primary: "#e65100",
|
primaryText: "#000000",
|
||||||
primaryText: "#ffffff",
|
primaryContainer: "#4a0e0e",
|
||||||
primaryContainer: "#ffecb3",
|
secondary: "#f28b82",
|
||||||
secondary: "#ff9800",
|
surface: "#1a1110",
|
||||||
surface: "#fff8f6",
|
surfaceText: "#f1dedc",
|
||||||
surfaceText: "#221a16",
|
surfaceVariant: "#534341",
|
||||||
surfaceVariant: "#f4ded5",
|
surfaceVariantText: "#d8c2be",
|
||||||
surfaceVariantText: "#52443d",
|
surfaceTint: "#f28b82",
|
||||||
surfaceTint: "#e65100",
|
background: "#1a1110",
|
||||||
background: "#fff8f6",
|
backgroundText: "#f1dedc",
|
||||||
backgroundText: "#221a16",
|
outline: "#a08c89",
|
||||||
outline: "#85736c",
|
surfaceContainer: "#271d1c",
|
||||||
surfaceContainer: "#fceae3",
|
surfaceContainerHigh: "#322826",
|
||||||
surfaceContainerHigh: "#f6e5de",
|
surfaceContainerHighest: "#3d3231",
|
||||||
surfaceContainerHighest: "#f0dfd8"
|
},
|
||||||
},
|
cyan: {
|
||||||
red: {
|
name: "Cyan",
|
||||||
name: "Red Light",
|
primary: "#00bcd4",
|
||||||
primary: "#d32f2f",
|
primaryText: "#000000",
|
||||||
primaryText: "#ffffff",
|
primaryContainer: "#004d5c",
|
||||||
primaryContainer: "#ffebee",
|
secondary: "#4dd0e1",
|
||||||
secondary: "#f44336",
|
surface: "#0e1416",
|
||||||
surface: "#fff8f7",
|
surfaceText: "#dee3e5",
|
||||||
surfaceText: "#231918",
|
surfaceVariant: "#3f484a",
|
||||||
surfaceVariant: "#f5ddda",
|
surfaceVariantText: "#bfc8ca",
|
||||||
surfaceVariantText: "#534341",
|
surfaceTint: "#4dd0e1",
|
||||||
surfaceTint: "#d32f2f",
|
background: "#0e1416",
|
||||||
background: "#fff8f7",
|
backgroundText: "#dee3e5",
|
||||||
backgroundText: "#231918",
|
outline: "#899295",
|
||||||
outline: "#857370",
|
surfaceContainer: "#1b2122",
|
||||||
surfaceContainer: "#fceae7",
|
surfaceContainerHigh: "#252b2c",
|
||||||
surfaceContainerHigh: "#f7e4e1",
|
surfaceContainerHighest: "#303637",
|
||||||
surfaceContainerHighest: "#f1dedc"
|
},
|
||||||
},
|
pink: {
|
||||||
cyan: {
|
name: "Pink",
|
||||||
name: "Cyan Light",
|
primary: "#e91e63",
|
||||||
primary: "#0097a7",
|
primaryText: "#000000",
|
||||||
primaryText: "#ffffff",
|
primaryContainer: "#4a0e2f",
|
||||||
primaryContainer: "#e0f2f1",
|
secondary: "#f8bbd9",
|
||||||
secondary: "#00bcd4",
|
surface: "#191112",
|
||||||
surface: "#f5fafc",
|
surfaceText: "#f0dee0",
|
||||||
surfaceText: "#171d1e",
|
surfaceVariant: "#524345",
|
||||||
surfaceVariant: "#dbe4e6",
|
surfaceVariantText: "#d6c2c3",
|
||||||
surfaceVariantText: "#3f484a",
|
surfaceTint: "#f8bbd9",
|
||||||
surfaceTint: "#0097a7",
|
background: "#191112",
|
||||||
background: "#f5fafc",
|
backgroundText: "#f0dee0",
|
||||||
backgroundText: "#171d1e",
|
outline: "#9f8c8e",
|
||||||
outline: "#6f797b",
|
surfaceContainer: "#261d1e",
|
||||||
surfaceContainer: "#e9eff0",
|
surfaceContainerHigh: "#312829",
|
||||||
surfaceContainerHigh: "#e3e9eb",
|
surfaceContainerHighest: "#3c3233",
|
||||||
surfaceContainerHighest: "#dee3e5"
|
},
|
||||||
},
|
amber: {
|
||||||
pink: {
|
name: "Amber",
|
||||||
name: "Pink Light",
|
primary: "#ffc107",
|
||||||
primary: "#c2185b",
|
primaryText: "#000000",
|
||||||
primaryText: "#ffffff",
|
primaryContainer: "#4a3c00",
|
||||||
primaryContainer: "#fce4ec",
|
secondary: "#ffd54f",
|
||||||
secondary: "#e91e63",
|
surface: "#17130b",
|
||||||
surface: "#fff8f7",
|
surfaceText: "#ebe1d4",
|
||||||
surfaceText: "#22191a",
|
surfaceVariant: "#4d4639",
|
||||||
surfaceVariant: "#f3dddf",
|
surfaceVariantText: "#d0c5b4",
|
||||||
surfaceVariantText: "#524345",
|
surfaceTint: "#ffd54f",
|
||||||
surfaceTint: "#c2185b",
|
background: "#17130b",
|
||||||
background: "#fff8f7",
|
backgroundText: "#ebe1d4",
|
||||||
backgroundText: "#22191a",
|
outline: "#998f80",
|
||||||
outline: "#847375",
|
surfaceContainer: "#231f17",
|
||||||
surfaceContainer: "#fbeaeb",
|
surfaceContainerHigh: "#2e2921",
|
||||||
surfaceContainerHigh: "#f5e4e5",
|
surfaceContainerHighest: "#39342b",
|
||||||
surfaceContainerHighest: "#f0dee0"
|
},
|
||||||
},
|
coral: {
|
||||||
amber: {
|
name: "Coral",
|
||||||
name: "Amber Light",
|
primary: "#ffb4ab",
|
||||||
primary: "#ff8f00",
|
primaryText: "#000000",
|
||||||
primaryText: "#000000",
|
primaryContainer: "#8c1d18",
|
||||||
primaryContainer: "#fff8e1",
|
secondary: "#f9dedc",
|
||||||
secondary: "#ffc107",
|
surface: "#1a1110",
|
||||||
surface: "#fff8f2",
|
surfaceText: "#f1dedc",
|
||||||
surfaceText: "#1f1b13",
|
surfaceVariant: "#534341",
|
||||||
surfaceVariant: "#ede1cf",
|
surfaceVariantText: "#d8c2bf",
|
||||||
surfaceVariantText: "#4d4639",
|
surfaceTint: "#ffb4ab",
|
||||||
surfaceTint: "#ff8f00",
|
background: "#1a1110",
|
||||||
background: "#fff8f2",
|
backgroundText: "#f1dedc",
|
||||||
backgroundText: "#1f1b13",
|
outline: "#a08c8a",
|
||||||
outline: "#7f7667",
|
surfaceContainer: "#271d1c",
|
||||||
surfaceContainer: "#f6ecdf",
|
surfaceContainerHigh: "#322826",
|
||||||
surfaceContainerHigh: "#f1e7d9",
|
surfaceContainerHighest: "#3d3231",
|
||||||
surfaceContainerHighest: "#ebe1d4"
|
},
|
||||||
},
|
monochrome: {
|
||||||
coral: {
|
name: "Monochrome",
|
||||||
name: "Coral Light",
|
primary: "#ffffff",
|
||||||
primary: "#8c1d18",
|
primaryText: "#2b303c",
|
||||||
primaryText: "#ffffff",
|
primaryContainer: "#424753",
|
||||||
primaryContainer: "#ffdad6",
|
secondary: "#c4c6d0",
|
||||||
secondary: "#ff5449",
|
surface: "#2a2a2a",
|
||||||
surface: "#fff8f7",
|
surfaceText: "#e4e2e3",
|
||||||
surfaceText: "#231918",
|
surfaceVariant: "#474648",
|
||||||
surfaceVariant: "#f5ddda",
|
surfaceVariantText: "#c8c6c7",
|
||||||
surfaceVariantText: "#534341",
|
surfaceTint: "#c2c6d6",
|
||||||
surfaceTint: "#8c1d18",
|
background: "#131315",
|
||||||
background: "#fff8f7",
|
backgroundText: "#e4e2e3",
|
||||||
backgroundText: "#231918",
|
outline: "#929092",
|
||||||
outline: "#857371",
|
surfaceContainer: "#353535",
|
||||||
surfaceContainer: "#fceae7",
|
surfaceContainerHigh: "#424242",
|
||||||
surfaceContainerHigh: "#f6e4e2",
|
surfaceContainerHighest: "#505050",
|
||||||
surfaceContainerHighest: "#f1dedc"
|
error: "#ffb4ab",
|
||||||
},
|
warning: "#3f4759",
|
||||||
monochrome: {
|
info: "#595e6c",
|
||||||
name: "Monochrome Light",
|
matugen_type: "scheme-monochrome",
|
||||||
primary: "#2b303c",
|
},
|
||||||
primaryText: "#ffffff",
|
},
|
||||||
primaryContainer: "#d6d7dc",
|
LIGHT: {
|
||||||
secondary: "#4a4d56",
|
blue: {
|
||||||
surface: "#f5f5f6",
|
name: "Blue Light",
|
||||||
surfaceText: "#2a2a2a",
|
primary: "#1976d2",
|
||||||
surfaceVariant: "#e0e0e2",
|
primaryText: "#ffffff",
|
||||||
surfaceVariantText: "#424242",
|
primaryContainer: "#e3f2fd",
|
||||||
surfaceTint: "#5a5f6e",
|
secondary: "#42a5f5",
|
||||||
background: "#ffffff",
|
surface: "#f7f9ff",
|
||||||
backgroundText: "#1a1a1a",
|
surfaceText: "#181c20",
|
||||||
outline: "#757577",
|
surfaceVariant: "#dee3eb",
|
||||||
surfaceContainer: "#e8e8ea",
|
surfaceVariantText: "#42474e",
|
||||||
surfaceContainerHigh: "#dcdcde",
|
surfaceTint: "#1976d2",
|
||||||
surfaceContainerHighest: "#d0d0d2",
|
background: "#f7f9ff",
|
||||||
error: "#ba1a1a",
|
backgroundText: "#181c20",
|
||||||
warning: "#f9e79f",
|
outline: "#72777f",
|
||||||
info: "#5d6475",
|
surfaceContainer: "#eceef4",
|
||||||
matugen_type: "scheme-monochrome"
|
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 = {
|
const ThemeCategories = {
|
||||||
GENERIC: {
|
GENERIC: {
|
||||||
name: "Generic",
|
name: "Generic",
|
||||||
variants: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"]
|
variants: [
|
||||||
},
|
"blue",
|
||||||
CATPPUCCIN: {
|
"purple",
|
||||||
name: "Catppuccin",
|
"green",
|
||||||
variants: Object.keys(CatppuccinVariants)
|
"orange",
|
||||||
}
|
"red",
|
||||||
}
|
"cyan",
|
||||||
|
"pink",
|
||||||
|
"amber",
|
||||||
|
"coral",
|
||||||
|
"monochrome",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const ThemeNames = {
|
const ThemeNames = {
|
||||||
BLUE: "blue",
|
BLUE: "blue",
|
||||||
PURPLE: "purple",
|
PURPLE: "purple",
|
||||||
GREEN: "green",
|
GREEN: "green",
|
||||||
ORANGE: "orange",
|
ORANGE: "orange",
|
||||||
RED: "red",
|
RED: "red",
|
||||||
CYAN: "cyan",
|
CYAN: "cyan",
|
||||||
PINK: "pink",
|
PINK: "pink",
|
||||||
AMBER: "amber",
|
AMBER: "amber",
|
||||||
CORAL: "coral",
|
CORAL: "coral",
|
||||||
MONOCHROME: "monochrome",
|
MONOCHROME: "monochrome",
|
||||||
DYNAMIC: "dynamic"
|
DYNAMIC: "dynamic",
|
||||||
}
|
};
|
||||||
|
|
||||||
function isStockTheme(themeName) {
|
function isStockTheme(themeName) {
|
||||||
return Object.keys(StockThemes.DARK).includes(themeName)
|
return Object.keys(StockThemes.DARK).includes(themeName);
|
||||||
}
|
|
||||||
|
|
||||||
function isCatppuccinVariant(themeName) {
|
|
||||||
return Object.keys(CatppuccinVariants).includes(themeName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAvailableThemes(isLight = false) {
|
function getAvailableThemes(isLight = false) {
|
||||||
return isLight ? StockThemes.LIGHT : StockThemes.DARK
|
return isLight ? StockThemes.LIGHT : StockThemes.DARK;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getThemeByName(themeName, isLight = false) {
|
function getThemeByName(themeName, isLight = false) {
|
||||||
if (isCatppuccinVariant(themeName)) {
|
const themes = getAvailableThemes(isLight);
|
||||||
return getCatppuccinTheme(themeName, isLight)
|
return themes[themeName] || themes.blue;
|
||||||
}
|
|
||||||
const themes = getAvailableThemes(isLight)
|
|
||||||
return themes[themeName] || themes.blue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllThemeNames() {
|
function getAllThemeNames() {
|
||||||
return Object.keys(StockThemes.DARK)
|
return Object.keys(StockThemes.DARK);
|
||||||
}
|
|
||||||
|
|
||||||
function getCatppuccinVariantNames() {
|
|
||||||
return Object.keys(CatppuccinVariants)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getThemeCategories() {
|
function getThemeCategories() {
|
||||||
return ThemeCategories
|
return ThemeCategories;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ Singleton {
|
|||||||
property var matugenColors: ({})
|
property var matugenColors: ({})
|
||||||
property var _pendingGenerateParams: null
|
property var _pendingGenerateParams: null
|
||||||
property var customThemeData: null
|
property var customThemeData: null
|
||||||
|
property var customThemeRawData: null
|
||||||
|
readonly property var currentThemeVariants: customThemeRawData?.variants || null
|
||||||
|
readonly property string currentThemeId: customThemeRawData?.id || ""
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Quickshell.execDetached(["mkdir", "-p", stateDir]);
|
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 outlineVariant: currentThemeData.outlineVariant || Qt.rgba(outline.r, outline.g, outline.b, 0.6)
|
||||||
property color surfaceContainer: currentThemeData.surfaceContainer
|
property color surfaceContainer: currentThemeData.surfaceContainer
|
||||||
property color surfaceContainerHigh: currentThemeData.surfaceContainerHigh
|
property color surfaceContainerHigh: currentThemeData.surfaceContainerHigh
|
||||||
property color surfaceContainerHighest: currentThemeData.surfaceContainerHighest
|
property color surfaceContainerHighest: currentThemeData.surfaceContainerHighest || surfaceContainerHigh
|
||||||
|
|
||||||
property color onSurface: surfaceText
|
property color onSurface: surfaceText
|
||||||
property color onSurfaceVariant: surfaceVariantText
|
property color onSurfaceVariant: surfaceVariantText
|
||||||
@@ -474,24 +477,28 @@ Singleton {
|
|||||||
|
|
||||||
if (themeName === dynamic) {
|
if (themeName === dynamic) {
|
||||||
currentTheme = dynamic;
|
currentTheme = dynamic;
|
||||||
currentThemeCategory = dynamic;
|
if (currentThemeCategory !== "registry")
|
||||||
|
currentThemeCategory = dynamic;
|
||||||
} else if (themeName === custom) {
|
} else if (themeName === custom) {
|
||||||
currentTheme = custom;
|
currentTheme = custom;
|
||||||
currentThemeCategory = custom;
|
if (currentThemeCategory !== "registry")
|
||||||
|
currentThemeCategory = custom;
|
||||||
if (typeof SettingsData !== "undefined" && SettingsData.customThemeFile) {
|
if (typeof SettingsData !== "undefined" && SettingsData.customThemeFile) {
|
||||||
loadCustomThemeFromFile(SettingsData.customThemeFile);
|
loadCustomThemeFromFile(SettingsData.customThemeFile);
|
||||||
}
|
}
|
||||||
|
} else if (themeName === "" && currentThemeCategory === "registry") {
|
||||||
|
// Registry category selected but no theme chosen yet
|
||||||
} else {
|
} else {
|
||||||
currentTheme = themeName;
|
currentTheme = themeName;
|
||||||
if (StockThemes.isCatppuccinVariant(themeName)) {
|
if (currentThemeCategory !== "registry") {
|
||||||
currentThemeCategory = "catppuccin";
|
|
||||||
} else {
|
|
||||||
currentThemeCategory = "generic";
|
currentThemeCategory = "generic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode);
|
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);
|
SettingsData.set("currentThemeName", currentTheme);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isGreeterMode) {
|
if (!isGreeterMode) {
|
||||||
generateSystemThemesFromCurrentTheme();
|
generateSystemThemesFromCurrentTheme();
|
||||||
@@ -553,62 +560,103 @@ Singleton {
|
|||||||
themeCategoryTransitionTimer.restart();
|
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) {
|
function loadCustomTheme(themeData) {
|
||||||
|
customThemeRawData = themeData;
|
||||||
|
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark";
|
||||||
|
|
||||||
|
var baseColors = {};
|
||||||
if (themeData.dark || themeData.light) {
|
if (themeData.dark || themeData.light) {
|
||||||
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark";
|
baseColors = themeData[colorMode] || themeData.dark || themeData.light || {};
|
||||||
const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light;
|
|
||||||
customThemeData = selectedTheme;
|
|
||||||
} else {
|
} 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();
|
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) {
|
function loadCustomThemeFromFile(filePath) {
|
||||||
customThemeFileView.path = filePath;
|
customThemeFileView.path = filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reloadCustomThemeVariant() {
|
||||||
|
if (currentTheme !== "custom" || !customThemeRawData)
|
||||||
|
return;
|
||||||
|
loadCustomTheme(customThemeRawData);
|
||||||
|
}
|
||||||
|
|
||||||
property alias availableThemeNames: root._availableThemeNames
|
property alias availableThemeNames: root._availableThemeNames
|
||||||
readonly property var _availableThemeNames: StockThemes.getAllThemeNames()
|
readonly property var _availableThemeNames: StockThemes.getAllThemeNames()
|
||||||
property string currentThemeName: currentTheme
|
property string currentThemeName: currentTheme
|
||||||
@@ -639,11 +687,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var widgetBaseHoverColor: {
|
property alias widgetBaseHoverColor: root.primaryPressed
|
||||||
const baseColor = widgetBaseBackgroundColor;
|
|
||||||
const factor = 1.2;
|
|
||||||
return isLightMode ? Qt.darker(baseColor, factor) : Qt.lighter(baseColor, factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
property color widgetIconColor: {
|
property color widgetIconColor: {
|
||||||
if (typeof SettingsData === "undefined") {
|
if (typeof SettingsData === "undefined") {
|
||||||
@@ -829,7 +873,7 @@ Singleton {
|
|||||||
if (typeof SettingsData !== "undefined") {
|
if (typeof SettingsData !== "undefined") {
|
||||||
const skipTemplates = [];
|
const skipTemplates = [];
|
||||||
if (!SettingsData.runDmsMatugenTemplates) {
|
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 {
|
} else {
|
||||||
if (!SettingsData.matugenTemplateGtk)
|
if (!SettingsData.matugenTemplateGtk)
|
||||||
skipTemplates.push("gtk");
|
skipTemplates.push("gtk");
|
||||||
@@ -845,12 +889,16 @@ Singleton {
|
|||||||
skipTemplates.push("pywalfox");
|
skipTemplates.push("pywalfox");
|
||||||
if (!SettingsData.matugenTemplateVesktop)
|
if (!SettingsData.matugenTemplateVesktop)
|
||||||
skipTemplates.push("vesktop");
|
skipTemplates.push("vesktop");
|
||||||
|
if (!SettingsData.matugenTemplateEquibop)
|
||||||
|
skipTemplates.push("equibop");
|
||||||
if (!SettingsData.matugenTemplateGhostty)
|
if (!SettingsData.matugenTemplateGhostty)
|
||||||
skipTemplates.push("ghostty");
|
skipTemplates.push("ghostty");
|
||||||
if (!SettingsData.matugenTemplateKitty)
|
if (!SettingsData.matugenTemplateKitty)
|
||||||
skipTemplates.push("kitty");
|
skipTemplates.push("kitty");
|
||||||
if (!SettingsData.matugenTemplateFoot)
|
if (!SettingsData.matugenTemplateFoot)
|
||||||
skipTemplates.push("foot");
|
skipTemplates.push("foot");
|
||||||
|
if (!SettingsData.matugenTemplateNeovim)
|
||||||
|
skipTemplates.push("nvim");
|
||||||
if (!SettingsData.matugenTemplateAlacritty)
|
if (!SettingsData.matugenTemplateAlacritty)
|
||||||
skipTemplates.push("alacritty");
|
skipTemplates.push("alacritty");
|
||||||
if (!SettingsData.matugenTemplateWezterm)
|
if (!SettingsData.matugenTemplateWezterm)
|
||||||
@@ -899,8 +947,48 @@ Singleton {
|
|||||||
|
|
||||||
let darkTheme, lightTheme;
|
let darkTheme, lightTheme;
|
||||||
if (currentTheme === "custom") {
|
if (currentTheme === "custom") {
|
||||||
darkTheme = customThemeData;
|
if (customThemeRawData && (customThemeRawData.dark || customThemeRawData.light)) {
|
||||||
lightTheme = customThemeData;
|
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 {
|
} else {
|
||||||
darkTheme = StockThemes.getThemeByName(currentTheme, false);
|
darkTheme = StockThemes.getThemeByName(currentTheme, false);
|
||||||
lightTheme = StockThemes.getThemeByName(currentTheme, true);
|
lightTheme = StockThemes.getThemeByName(currentTheme, true);
|
||||||
@@ -918,6 +1006,7 @@ Singleton {
|
|||||||
|
|
||||||
function buildMatugenColorsFromTheme(darkTheme, lightTheme) {
|
function buildMatugenColorsFromTheme(darkTheme, lightTheme) {
|
||||||
const colors = {};
|
const colors = {};
|
||||||
|
const isLight = SessionData !== "undefined" && SessionData.isLightMode;
|
||||||
|
|
||||||
function addColor(matugenKey, darkVal, lightVal) {
|
function addColor(matugenKey, darkVal, lightVal) {
|
||||||
if (!darkVal && !lightVal)
|
if (!darkVal && !lightVal)
|
||||||
@@ -930,7 +1019,7 @@ Singleton {
|
|||||||
"color": String(lightVal || darkVal)
|
"color": String(lightVal || darkVal)
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"color": String(darkVal || lightVal)
|
"color": String((isLight && lightVal) ? lightVal : darkVal)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ Singleton {
|
|||||||
showNetworkIcon: true,
|
showNetworkIcon: true,
|
||||||
showBluetoothIcon: true,
|
showBluetoothIcon: true,
|
||||||
showAudioIcon: true,
|
showAudioIcon: true,
|
||||||
|
showAudioPercent: true,
|
||||||
showVpnIcon: true,
|
showVpnIcon: true,
|
||||||
showBrightnessIcon: false,
|
showBrightnessIcon: false,
|
||||||
|
showBrightnessPercent: false,
|
||||||
showMicIcon: false,
|
showMicIcon: false,
|
||||||
|
showMicPercent: true,
|
||||||
showBatteryIcon: false,
|
showBatteryIcon: false,
|
||||||
showPrinterIcon: false
|
showPrinterIcon: false
|
||||||
};
|
};
|
||||||
@@ -65,12 +68,18 @@ Singleton {
|
|||||||
item.showBluetoothIcon = order[i].showBluetoothIcon;
|
item.showBluetoothIcon = order[i].showBluetoothIcon;
|
||||||
if (isObj && order[i].showAudioIcon !== undefined)
|
if (isObj && order[i].showAudioIcon !== undefined)
|
||||||
item.showAudioIcon = order[i].showAudioIcon;
|
item.showAudioIcon = order[i].showAudioIcon;
|
||||||
|
if (isObj && order[i].showAudioPercent !== undefined)
|
||||||
|
item.showAudioPercent = order[i].showAudioPercent;
|
||||||
if (isObj && order[i].showVpnIcon !== undefined)
|
if (isObj && order[i].showVpnIcon !== undefined)
|
||||||
item.showVpnIcon = order[i].showVpnIcon;
|
item.showVpnIcon = order[i].showVpnIcon;
|
||||||
if (isObj && order[i].showBrightnessIcon !== undefined)
|
if (isObj && order[i].showBrightnessIcon !== undefined)
|
||||||
item.showBrightnessIcon = order[i].showBrightnessIcon;
|
item.showBrightnessIcon = order[i].showBrightnessIcon;
|
||||||
|
if (isObj && order[i].showBrightnessPercent !== undefined)
|
||||||
|
item.showBrightnessPercent = order[i].showBrightnessPercent;
|
||||||
if (isObj && order[i].showMicIcon !== undefined)
|
if (isObj && order[i].showMicIcon !== undefined)
|
||||||
item.showMicIcon = order[i].showMicIcon;
|
item.showMicIcon = order[i].showMicIcon;
|
||||||
|
if (isObj && order[i].showMicPercent !== undefined)
|
||||||
|
item.showMicPercent = order[i].showMicPercent;
|
||||||
if (isObj && order[i].showBatteryIcon !== undefined)
|
if (isObj && order[i].showBatteryIcon !== undefined)
|
||||||
item.showBatteryIcon = order[i].showBatteryIcon;
|
item.showBatteryIcon = order[i].showBatteryIcon;
|
||||||
if (isObj && order[i].showPrinterIcon !== undefined)
|
if (isObj && order[i].showPrinterIcon !== undefined)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -11,61 +10,20 @@ Singleton {
|
|||||||
|
|
||||||
property var settingsRoot: null
|
property var settingsRoot: null
|
||||||
|
|
||||||
function detectIcons() {
|
|
||||||
systemDefaultDetectionProcess.running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function detectQtTools() {
|
function detectQtTools() {
|
||||||
qtToolsDetectionProcess.running = true
|
qtToolsDetectionProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectFprintd() {
|
function detectFprintd() {
|
||||||
fprintdDetectionProcess.running = true
|
fprintdDetectionProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPluginSettings() {
|
function checkPluginSettings() {
|
||||||
pluginSettingsCheckProcess.running = true
|
pluginSettingsCheckProcess.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDefaultSettings() {
|
function checkDefaultSettings() {
|
||||||
defaultSettingsCheckProcess.running = true
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
property var qtToolsDetectionProcess: Process {
|
property var qtToolsDetectionProcess: Process {
|
||||||
@@ -74,7 +32,8 @@ Singleton {
|
|||||||
|
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
if (!settingsRoot) return;
|
if (!settingsRoot)
|
||||||
|
return;
|
||||||
if (text && text.trim()) {
|
if (text && text.trim()) {
|
||||||
var lines = text.trim().split('\n');
|
var lines = text.trim().split('\n');
|
||||||
for (var i = 0; i < lines.length; i++) {
|
for (var i = 0; i < lines.length; i++) {
|
||||||
@@ -95,8 +54,9 @@ Singleton {
|
|||||||
property var defaultSettingsCheckProcess: Process {
|
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"]
|
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
|
running: false
|
||||||
onExited: function(exitCode) {
|
onExited: function (exitCode) {
|
||||||
if (!settingsRoot) return;
|
if (!settingsRoot)
|
||||||
|
return;
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
console.info("Copied default-settings.json to settings.json");
|
console.info("Copied default-settings.json to settings.json");
|
||||||
if (settingsRoot.settingsFile) {
|
if (settingsRoot.settingsFile) {
|
||||||
@@ -113,8 +73,9 @@ Singleton {
|
|||||||
property var fprintdDetectionProcess: Process {
|
property var fprintdDetectionProcess: Process {
|
||||||
command: ["sh", "-c", "command -v fprintd-list >/dev/null 2>&1"]
|
command: ["sh", "-c", "command -v fprintd-list >/dev/null 2>&1"]
|
||||||
running: false
|
running: false
|
||||||
onExited: function(exitCode) {
|
onExited: function (exitCode) {
|
||||||
if (!settingsRoot) return;
|
if (!settingsRoot)
|
||||||
|
return;
|
||||||
settingsRoot.fprintdAvailable = (exitCode === 0);
|
settingsRoot.fprintdAvailable = (exitCode === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,8 +84,9 @@ Singleton {
|
|||||||
command: ["test", "-f", settingsRoot?.pluginSettingsPath || ""]
|
command: ["test", "-f", settingsRoot?.pluginSettingsPath || ""]
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
onExited: function(exitCode) {
|
onExited: function (exitCode) {
|
||||||
if (!settingsRoot) return;
|
if (!settingsRoot)
|
||||||
|
return;
|
||||||
settingsRoot.pluginSettingsFileExists = (exitCode === 0);
|
settingsRoot.pluginSettingsFileExists = (exitCode === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ function percentToUnit(v) {
|
|||||||
|
|
||||||
var SPEC = {
|
var SPEC = {
|
||||||
currentThemeName: { def: "blue", onChange: "applyStoredTheme" },
|
currentThemeName: { def: "blue", onChange: "applyStoredTheme" },
|
||||||
|
currentThemeCategory: { def: "generic" },
|
||||||
customThemeFile: { def: "" },
|
customThemeFile: { def: "" },
|
||||||
|
registryThemeVariants: { def: {} },
|
||||||
matugenScheme: { def: "scheme-tonal-spot", onChange: "regenSystemThemes" },
|
matugenScheme: { def: "scheme-tonal-spot", onChange: "regenSystemThemes" },
|
||||||
runUserMatugenTemplates: { def: true, onChange: "regenSystemThemes" },
|
runUserMatugenTemplates: { def: true, onChange: "regenSystemThemes" },
|
||||||
matugenTargetMonitor: { def: "", onChange: "regenSystemThemes" },
|
matugenTargetMonitor: { def: "", onChange: "regenSystemThemes" },
|
||||||
@@ -18,6 +20,8 @@ var SPEC = {
|
|||||||
widgetBackgroundColor: { def: "sch" },
|
widgetBackgroundColor: { def: "sch" },
|
||||||
widgetColorMode: { def: "default" },
|
widgetColorMode: { def: "default" },
|
||||||
cornerRadius: { def: 12, onChange: "updateNiriLayout" },
|
cornerRadius: { def: 12, onChange: "updateNiriLayout" },
|
||||||
|
niriLayoutGapsOverride: { def: -1, onChange: "updateNiriLayout" },
|
||||||
|
niriLayoutRadiusOverride: { def: -1, onChange: "updateNiriLayout" },
|
||||||
|
|
||||||
use24HourClock: { def: true },
|
use24HourClock: { def: true },
|
||||||
showSeconds: { def: false },
|
showSeconds: { def: false },
|
||||||
@@ -51,9 +55,12 @@ var SPEC = {
|
|||||||
controlCenterShowNetworkIcon: { def: true },
|
controlCenterShowNetworkIcon: { def: true },
|
||||||
controlCenterShowBluetoothIcon: { def: true },
|
controlCenterShowBluetoothIcon: { def: true },
|
||||||
controlCenterShowAudioIcon: { def: true },
|
controlCenterShowAudioIcon: { def: true },
|
||||||
|
controlCenterShowAudioPercent: { def: false },
|
||||||
controlCenterShowVpnIcon: { def: true },
|
controlCenterShowVpnIcon: { def: true },
|
||||||
controlCenterShowBrightnessIcon: { def: false },
|
controlCenterShowBrightnessIcon: { def: false },
|
||||||
|
controlCenterShowBrightnessPercent: { def: false },
|
||||||
controlCenterShowMicIcon: { def: false },
|
controlCenterShowMicIcon: { def: false },
|
||||||
|
controlCenterShowMicPercent: { def: false },
|
||||||
controlCenterShowBatteryIcon: { def: false },
|
controlCenterShowBatteryIcon: { def: false },
|
||||||
controlCenterShowPrinterIcon: { def: false },
|
controlCenterShowPrinterIcon: { def: false },
|
||||||
|
|
||||||
@@ -85,6 +92,7 @@ var SPEC = {
|
|||||||
waveProgressEnabled: { def: true },
|
waveProgressEnabled: { def: true },
|
||||||
scrollTitleEnabled: { def: true },
|
scrollTitleEnabled: { def: true },
|
||||||
audioVisualizerEnabled: { def: true },
|
audioVisualizerEnabled: { def: true },
|
||||||
|
audioScrollEnabled: { def: true },
|
||||||
clockCompactMode: { def: false },
|
clockCompactMode: { def: false },
|
||||||
focusedWindowCompactMode: { def: false },
|
focusedWindowCompactMode: { def: false },
|
||||||
runningAppsCompactMode: { def: true },
|
runningAppsCompactMode: { def: true },
|
||||||
@@ -154,6 +162,7 @@ var SPEC = {
|
|||||||
batterySuspendTimeout: { def: 0 },
|
batterySuspendTimeout: { def: 0 },
|
||||||
batterySuspendBehavior: { def: 0 },
|
batterySuspendBehavior: { def: 0 },
|
||||||
batteryProfileName: { def: "" },
|
batteryProfileName: { def: "" },
|
||||||
|
batteryChargeLimit: { def: 100 },
|
||||||
lockBeforeSuspend: { def: false },
|
lockBeforeSuspend: { def: false },
|
||||||
loginctlLockIntegration: { def: true },
|
loginctlLockIntegration: { def: true },
|
||||||
fadeToLockEnabled: { def: false },
|
fadeToLockEnabled: { def: false },
|
||||||
@@ -178,10 +187,12 @@ var SPEC = {
|
|||||||
matugenTemplateFirefox: { def: true },
|
matugenTemplateFirefox: { def: true },
|
||||||
matugenTemplatePywalfox: { def: true },
|
matugenTemplatePywalfox: { def: true },
|
||||||
matugenTemplateVesktop: { def: true },
|
matugenTemplateVesktop: { def: true },
|
||||||
|
matugenTemplateEquibop: { def: true },
|
||||||
matugenTemplateGhostty: { def: true },
|
matugenTemplateGhostty: { def: true },
|
||||||
matugenTemplateKitty: { def: true },
|
matugenTemplateKitty: { def: true },
|
||||||
matugenTemplateFoot: { def: true },
|
matugenTemplateFoot: { def: true },
|
||||||
matugenTemplateAlacritty: { def: true },
|
matugenTemplateAlacritty: { def: true },
|
||||||
|
matugenTemplateNeovim: { def: true },
|
||||||
matugenTemplateWezterm: { def: true },
|
matugenTemplateWezterm: { def: true },
|
||||||
matugenTemplateDgop: { def: true },
|
matugenTemplateDgop: { def: true },
|
||||||
matugenTemplateKcolorscheme: { def: true },
|
matugenTemplateKcolorscheme: { def: true },
|
||||||
@@ -201,6 +212,7 @@ var SPEC = {
|
|||||||
dockBorderColor: { def: "surfaceText" },
|
dockBorderColor: { def: "surfaceText" },
|
||||||
dockBorderOpacity: { def: 1.0, coerce: percentToUnit },
|
dockBorderOpacity: { def: 1.0, coerce: percentToUnit },
|
||||||
dockBorderThickness: { def: 1 },
|
dockBorderThickness: { def: 1 },
|
||||||
|
dockIsolateDisplays: { def: false },
|
||||||
|
|
||||||
notificationOverlayEnabled: { def: false },
|
notificationOverlayEnabled: { def: false },
|
||||||
overviewRows: { def: 2, persist: false },
|
overviewRows: { def: 2, persist: false },
|
||||||
@@ -258,6 +270,8 @@ var SPEC = {
|
|||||||
displayNameMode: { def: "system" },
|
displayNameMode: { def: "system" },
|
||||||
screenPreferences: { def: {} },
|
screenPreferences: { def: {} },
|
||||||
showOnLastDisplay: { def: {} },
|
showOnLastDisplay: { def: {} },
|
||||||
|
niriOutputSettings: { def: {} },
|
||||||
|
hyprlandOutputSettings: { def: {} },
|
||||||
|
|
||||||
barConfigs: { def: [{
|
barConfigs: { def: [{
|
||||||
id: "default",
|
id: "default",
|
||||||
@@ -290,6 +304,7 @@ var SPEC = {
|
|||||||
fontScale: 1.0,
|
fontScale: 1.0,
|
||||||
autoHide: false,
|
autoHide: false,
|
||||||
autoHideDelay: 250,
|
autoHideDelay: 250,
|
||||||
|
showOnWindowsOpen: false,
|
||||||
openOnOverview: false,
|
openOnOverview: false,
|
||||||
visible: true,
|
visible: true,
|
||||||
popupGapsAuto: true,
|
popupGapsAuto: true,
|
||||||
@@ -298,7 +313,52 @@ var SPEC = {
|
|||||||
scrollEnabled: true,
|
scrollEnabled: true,
|
||||||
scrollXBehavior: "column",
|
scrollXBehavior: "column",
|
||||||
scrollYBehavior: "workspace"
|
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() {
|
function getValidKeys() {
|
||||||
|
|||||||
@@ -119,6 +119,101 @@ function migrateToVersion(obj, targetVersion) {
|
|||||||
settings.configVersion = 3;
|
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;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ Item {
|
|||||||
|
|
||||||
WallpaperBackground {}
|
WallpaperBackground {}
|
||||||
|
|
||||||
|
DesktopWidgetLayer {}
|
||||||
|
|
||||||
Lock {
|
Lock {
|
||||||
id: lock
|
id: lock
|
||||||
}
|
}
|
||||||
@@ -508,6 +510,22 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
target: DMSService
|
target: DMSService
|
||||||
function onOpenUrlRequested(url) {
|
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.url = url;
|
||||||
browserPickerModal.open();
|
browserPickerModal.open();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -893,4 +893,58 @@ Item {
|
|||||||
|
|
||||||
target: "clipboard"
|
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 var customKeyboardFocus: null
|
||||||
property bool useOverlayLayer: false
|
property bool useOverlayLayer: false
|
||||||
readonly property alias contentWindow: contentWindow
|
readonly property alias contentWindow: contentWindow
|
||||||
readonly property alias backgroundWindow: backgroundWindow
|
readonly property alias clickCatcher: clickCatcher
|
||||||
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
|
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
|
||||||
|
readonly property bool useBackground: showBackground && SettingsData.modalDarkenBackground
|
||||||
readonly property bool useSingleWindow: root.useHyprlandFocusGrab
|
readonly property bool useSingleWindow: useHyprlandFocusGrab || useBackground
|
||||||
|
|
||||||
signal opened
|
signal opened
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
signal backgroundClicked
|
signal backgroundClicked
|
||||||
|
|
||||||
property bool animationsEnabled: true
|
property bool animationsEnabled: true
|
||||||
readonly property bool useBackgroundWindow: !useSingleWindow
|
|
||||||
|
|
||||||
function open() {
|
function open() {
|
||||||
ModalManager.openModal(root);
|
ModalManager.openModal(root);
|
||||||
@@ -64,25 +63,21 @@ Item {
|
|||||||
const focusedScreen = CompositorService.getFocusedScreen();
|
const focusedScreen = CompositorService.getFocusedScreen();
|
||||||
if (focusedScreen) {
|
if (focusedScreen) {
|
||||||
contentWindow.screen = focusedScreen;
|
contentWindow.screen = focusedScreen;
|
||||||
if (useBackgroundWindow)
|
if (!useSingleWindow)
|
||||||
backgroundWindow.screen = focusedScreen;
|
clickCatcher.screen = focusedScreen;
|
||||||
}
|
}
|
||||||
shouldBeVisible = true;
|
shouldBeVisible = true;
|
||||||
contentWindow.visible = false;
|
if (!useSingleWindow)
|
||||||
if (useBackgroundWindow)
|
clickCatcher.visible = true;
|
||||||
backgroundWindow.visible = true;
|
contentWindow.visible = true;
|
||||||
Qt.callLater(() => {
|
shouldHaveFocus = false;
|
||||||
contentWindow.visible = true;
|
Qt.callLater(() => shouldHaveFocus = Qt.binding(() => shouldBeVisible));
|
||||||
shouldHaveFocus = false;
|
|
||||||
Qt.callLater(() => {
|
|
||||||
shouldHaveFocus = Qt.binding(() => shouldBeVisible);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
shouldBeVisible = false;
|
shouldBeVisible = false;
|
||||||
shouldHaveFocus = false;
|
shouldHaveFocus = false;
|
||||||
|
ModalManager.closeModal(root);
|
||||||
closeTimer.restart();
|
closeTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,10 +85,11 @@ Item {
|
|||||||
animationsEnabled = false;
|
animationsEnabled = false;
|
||||||
shouldBeVisible = false;
|
shouldBeVisible = false;
|
||||||
shouldHaveFocus = false;
|
shouldHaveFocus = false;
|
||||||
|
ModalManager.closeModal(root);
|
||||||
closeTimer.stop();
|
closeTimer.stop();
|
||||||
contentWindow.visible = false;
|
contentWindow.visible = false;
|
||||||
if (useBackgroundWindow)
|
if (!useSingleWindow)
|
||||||
backgroundWindow.visible = false;
|
clickCatcher.visible = false;
|
||||||
dialogClosed();
|
dialogClosed();
|
||||||
Qt.callLater(() => animationsEnabled = true);
|
Qt.callLater(() => animationsEnabled = true);
|
||||||
}
|
}
|
||||||
@@ -105,9 +101,8 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
target: ModalManager
|
target: ModalManager
|
||||||
function onCloseAllModalsExcept(excludedModal) {
|
function onCloseAllModalsExcept(excludedModal) {
|
||||||
if (excludedModal !== root && !allowStacking && shouldBeVisible) {
|
if (excludedModal !== root && !allowStacking && shouldBeVisible)
|
||||||
close();
|
close();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +124,8 @@ Item {
|
|||||||
const newScreen = CompositorService.getFocusedScreen();
|
const newScreen = CompositorService.getFocusedScreen();
|
||||||
if (newScreen) {
|
if (newScreen) {
|
||||||
contentWindow.screen = newScreen;
|
contentWindow.screen = newScreen;
|
||||||
if (useBackgroundWindow)
|
if (!useSingleWindow)
|
||||||
backgroundWindow.screen = newScreen;
|
clickCatcher.screen = newScreen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,12 +134,12 @@ Item {
|
|||||||
id: closeTimer
|
id: closeTimer
|
||||||
interval: animationDuration + 120
|
interval: animationDuration + 120
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (!shouldBeVisible) {
|
if (shouldBeVisible)
|
||||||
contentWindow.visible = false;
|
return;
|
||||||
if (useBackgroundWindow)
|
contentWindow.visible = false;
|
||||||
backgroundWindow.visible = false;
|
if (!useSingleWindow)
|
||||||
dialogClosed();
|
clickCatcher.visible = false;
|
||||||
}
|
dialogClosed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,11 +174,11 @@ Item {
|
|||||||
})(), dpr)
|
})(), dpr)
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: backgroundWindow
|
id: clickCatcher
|
||||||
visible: false
|
visible: false
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
WlrLayershell.namespace: root.layerNamespace + ":background"
|
WlrLayershell.namespace: root.layerNamespace + ":clickcatcher"
|
||||||
WlrLayershell.layer: WlrLayershell.Top
|
WlrLayershell.layer: WlrLayershell.Top
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
@@ -199,41 +194,16 @@ Item {
|
|||||||
item: Rectangle {
|
item: Rectangle {
|
||||||
x: root.alignedX
|
x: root.alignedX
|
||||||
y: root.alignedY
|
y: root.alignedY
|
||||||
width: root.shouldBeVisible ? root.alignedWidth : 0
|
width: root.alignedWidth
|
||||||
height: root.shouldBeVisible ? root.alignedHeight : 0
|
height: root.alignedHeight
|
||||||
}
|
}
|
||||||
intersection: Intersection.Xor
|
intersection: Intersection.Xor
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: root.useBackgroundWindow && root.closeOnBackgroundClick && root.shouldBeVisible
|
enabled: root.closeOnBackgroundClick && root.shouldBeVisible
|
||||||
onClicked: mouse => {
|
onClicked: root.backgroundClicked()
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,8 +243,8 @@ Item {
|
|||||||
anchors {
|
anchors {
|
||||||
left: true
|
left: true
|
||||||
top: true
|
top: true
|
||||||
right: root.useSingleWindow ? true : undefined
|
right: root.useSingleWindow
|
||||||
bottom: root.useSingleWindow ? true : undefined
|
bottom: root.useSingleWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.margins {
|
WlrLayershell.margins {
|
||||||
@@ -284,8 +254,8 @@ Item {
|
|||||||
bottom: 0
|
bottom: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitWidth: root.useSingleWindow ? undefined : root.alignedWidth + (shadowBuffer * 2)
|
implicitWidth: root.useSingleWindow ? 0 : root.alignedWidth + (shadowBuffer * 2)
|
||||||
implicitHeight: root.useSingleWindow ? undefined : root.alignedHeight + (shadowBuffer * 2)
|
implicitHeight: root.useSingleWindow ? 0 : root.alignedHeight + (shadowBuffer * 2)
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
@@ -309,8 +279,8 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
z: -1
|
z: -1
|
||||||
color: "black"
|
color: "black"
|
||||||
opacity: root.showBackground && SettingsData.modalDarkenBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
opacity: root.useBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
||||||
visible: root.useSingleWindow && root.showBackground && SettingsData.modalDarkenBackground
|
visible: root.useBackground
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
enabled: root.animationsEnabled
|
enabled: root.animationsEnabled
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ DankModal {
|
|||||||
FocusScope {
|
FocusScope {
|
||||||
id: colorContent
|
id: colorContent
|
||||||
|
|
||||||
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
property alias hexInput: hexInput
|
property alias hexInput: hexInput
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -160,12 +163,14 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: I18n.tr("Select a color from the palette or use custom sliders")
|
text: I18n.tr("Select a color from the palette or use custom sliders")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +365,7 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
|
|
||||||
GridView {
|
GridView {
|
||||||
@@ -410,6 +416,7 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -462,6 +469,7 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
|
|
||||||
DankSlider {
|
DankSlider {
|
||||||
@@ -507,6 +515,7 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -566,6 +575,7 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -630,6 +640,7 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceTextMedium
|
color: Theme.surfaceTextMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ DankModal {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string outputName: ""
|
property string outputName: ""
|
||||||
property var position: undefined
|
property var changes: []
|
||||||
property var mode: undefined
|
property int countdown: 10
|
||||||
property var vrr: undefined
|
|
||||||
property int countdown: 15
|
signal confirmed
|
||||||
|
signal reverted
|
||||||
|
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
allowStacking: true
|
allowStacking: true
|
||||||
@@ -23,23 +24,27 @@ DankModal {
|
|||||||
repeat: true
|
repeat: true
|
||||||
running: root.shouldBeVisible
|
running: root.shouldBeVisible
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
countdown--;
|
root.countdown--;
|
||||||
if (countdown <= 0) {
|
if (root.countdown <= 0) {
|
||||||
revert();
|
root.reverted();
|
||||||
|
root.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
countdown = 15;
|
countdown = 10;
|
||||||
countdownTimer.start();
|
countdownTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
onClosed: {
|
onDialogClosed: {
|
||||||
countdownTimer.stop();
|
countdownTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
onBackgroundClicked: revert
|
onBackgroundClicked: {
|
||||||
|
root.reverted();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
FocusScope {
|
FocusScope {
|
||||||
@@ -50,12 +55,14 @@ DankModal {
|
|||||||
implicitHeight: mainColumn.implicitHeight
|
implicitHeight: mainColumn.implicitHeight
|
||||||
|
|
||||||
Keys.onEscapePressed: event => {
|
Keys.onEscapePressed: event => {
|
||||||
revert();
|
root.reverted();
|
||||||
|
root.close();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onReturnPressed: event => {
|
Keys.onReturnPressed: event => {
|
||||||
confirm();
|
root.confirmed();
|
||||||
|
root.close();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,81 +76,42 @@ DankModal {
|
|||||||
anchors.topMargin: Theme.spacingM
|
anchors.topMargin: Theme.spacingM
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
Column {
|
StyledText {
|
||||||
width: parent.width
|
text: I18n.tr("Confirm Display Changes")
|
||||||
spacing: Theme.spacingXS
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
StyledText {
|
font.weight: Font.Medium
|
||||||
text: I18n.tr("Confirm Display Changes")
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Display settings for ") + outputName
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceTextMedium
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 80
|
height: 70
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.surfaceContainerHighest
|
color: Theme.surfaceContainerHighest
|
||||||
|
|
||||||
Column {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 4
|
text: root.countdown + "s"
|
||||||
|
font.pixelSize: Theme.fontSizeXLarge * 1.5
|
||||||
StyledText {
|
color: Theme.primary
|
||||||
text: I18n.tr("Reverting in:")
|
font.weight: Font.Bold
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceTextMedium
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: countdown + "s"
|
|
||||||
font.pixelSize: Theme.fontSizeXLarge * 1.5
|
|
||||||
color: Theme.primary
|
|
||||||
font.weight: Font.Bold
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
visible: root.changes.length > 0
|
||||||
|
|
||||||
StyledText {
|
Repeater {
|
||||||
text: I18n.tr("Changes:")
|
model: root.changes
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceTextMedium
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
visible: position !== undefined && position !== null
|
required property var modelData
|
||||||
text: I18n.tr("Position: ") + (position ? position.x + ", " + position.y : "")
|
text: modelData
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceVariantText
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
|
||||||
visible: mode !== undefined && mode !== null && mode !== ""
|
|
||||||
text: I18n.tr("Mode: ") + (mode || "")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
visible: vrr !== undefined && vrr !== null
|
|
||||||
text: I18n.tr("VRR: ") + (vrr ? I18n.tr("Enabled") : I18n.tr("Disabled"))
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +148,10 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: revert
|
onClicked: {
|
||||||
|
root.reverted();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +177,10 @@ DankModal {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: confirm
|
onClicked: {
|
||||||
|
root.confirmed();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
@@ -228,18 +202,11 @@ DankModal {
|
|||||||
iconName: "close"
|
iconName: "close"
|
||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 4
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
onClicked: revert
|
onClicked: {
|
||||||
|
root.reverted();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirm() {
|
|
||||||
displaysTab.confirmChanges();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function revert() {
|
|
||||||
displaysTab.revertChanges();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user