mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-28 15:32:50 -05:00
Compare commits
3 Commits
da006b883e
...
displaycon
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9673078a75 | ||
|
|
9e8c93bfd7 | ||
|
|
43d6f4b1d3 |
294
.github/workflows/release.yml
vendored
294
.github/workflows/release.yml
vendored
@@ -398,3 +398,297 @@ 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
|
||||||
|
|||||||
216
.github/workflows/run-copr.yml
vendored
216
.github/workflows/run-copr.yml
vendored
@@ -3,17 +3,8 @@ 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., 1.0.3, leave empty for latest release)'
|
description: 'Versioning (e.g., 0.1.14, leave empty for latest release)'
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
release:
|
release:
|
||||||
@@ -22,27 +13,8 @@ on:
|
|||||||
default: '1'
|
default: '1'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
determine-packages:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
packages: ${{ steps.set-packages.outputs.packages }}
|
|
||||||
steps:
|
|
||||||
- name: Set package list
|
|
||||||
id: set-packages
|
|
||||||
run: |
|
|
||||||
PACKAGE_INPUT="${{ github.event.inputs.package || 'dms' }}"
|
|
||||||
if [ "$PACKAGE_INPUT" = "both" ]; then
|
|
||||||
echo 'packages=["dms","dms-greeter"]' >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "packages=[\"$PACKAGE_INPUT\"]" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
build-and-upload:
|
build-and-upload:
|
||||||
needs: determine-packages
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
package: ${{ fromJSON(needs.determine-packages.outputs.packages) }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -67,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "release=$RELEASE" >> $GITHUB_OUTPUT
|
echo "release=$RELEASE" >> $GITHUB_OUTPUT
|
||||||
echo "✅ Building ${{ matrix.package }} version: $VERSION-$RELEASE"
|
echo "✅ Building DMS hotfix version: $VERSION-$RELEASE"
|
||||||
|
|
||||||
- name: Setup build environment
|
- name: Setup build environment
|
||||||
run: |
|
run: |
|
||||||
@@ -98,31 +70,157 @@ 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 }}"
|
|
||||||
|
|
||||||
# Copy spec file from repository
|
cat > ~/rpmbuild/SPECS/dms.spec <<'SPECEOF'
|
||||||
cp distro/fedora/${PACKAGE}.spec ~/rpmbuild/SPECS/${PACKAGE}.spec
|
# Spec for DMS stable releases - Generated by GitHub Actions
|
||||||
|
|
||||||
# Replace placeholders with actual values
|
%global debug_package %{nil}
|
||||||
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/${PACKAGE}.spec
|
%global version VERSION_PLACEHOLDER
|
||||||
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/${PACKAGE}.spec
|
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
|
||||||
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/${PACKAGE}.spec
|
|
||||||
|
|
||||||
echo "✅ Spec file generated for ${PACKAGE} v${VERSION}-${RELEASE}"
|
Name: dms
|
||||||
|
Version: %{version}
|
||||||
|
Release: RELEASE_PLACEHOLDER%{?dist}
|
||||||
|
Summary: %{pkg_summary}
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
|
||||||
|
Source0: dms-qml.tar.gz
|
||||||
|
|
||||||
|
BuildRequires: gzip
|
||||||
|
BuildRequires: wget
|
||||||
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
|
Requires: (quickshell or quickshell-git)
|
||||||
|
Requires: accountsservice
|
||||||
|
Requires: dms-cli = %{version}-%{release}
|
||||||
|
Requires: dgop
|
||||||
|
|
||||||
|
Recommends: cava
|
||||||
|
Recommends: cliphist
|
||||||
|
Recommends: danksearch
|
||||||
|
Recommends: hyprpicker
|
||||||
|
Recommends: matugen
|
||||||
|
Recommends: wl-clipboard
|
||||||
|
Recommends: NetworkManager
|
||||||
|
Recommends: qt6-qtmultimedia
|
||||||
|
Suggests: qt6ct
|
||||||
|
|
||||||
|
%description
|
||||||
|
DankMaterialShell (DMS) is a modern Wayland desktop shell built with Quickshell
|
||||||
|
and optimized for the niri and hyprland compositors. Features notifications,
|
||||||
|
app launcher, wallpaper customization, and fully customizable with plugins.
|
||||||
|
|
||||||
|
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
|
||||||
|
process monitoring, notification center, clipboard history, dock, control center,
|
||||||
|
lock screen, and comprehensive plugin system.
|
||||||
|
|
||||||
|
%package -n dms-cli
|
||||||
|
Summary: DankMaterialShell CLI tool
|
||||||
|
License: MIT
|
||||||
|
URL: https://github.com/AvengeMedia/DankMaterialShell
|
||||||
|
|
||||||
|
%description -n dms-cli
|
||||||
|
Command-line interface for DankMaterialShell configuration and management.
|
||||||
|
Provides native DBus bindings, NetworkManager integration, and system utilities.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q -c -n dms-qml
|
||||||
|
|
||||||
|
# Download architecture-specific binaries during build
|
||||||
|
# This ensures the correct architecture is used for each build target
|
||||||
|
case "%{_arch}" in
|
||||||
|
x86_64)
|
||||||
|
ARCH_SUFFIX="amd64"
|
||||||
|
;;
|
||||||
|
aarch64)
|
||||||
|
ARCH_SUFFIX="arm64"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported architecture: %{_arch}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Download dms-cli for target architecture
|
||||||
|
wget -O %{_builddir}/dms-cli.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-${ARCH_SUFFIX}.gz" || {
|
||||||
|
echo "Failed to download dms-cli for architecture %{_arch}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
gunzip -c %{_builddir}/dms-cli.gz > %{_builddir}/dms-cli
|
||||||
|
chmod +x %{_builddir}/dms-cli
|
||||||
|
|
||||||
|
%build
|
||||||
|
|
||||||
|
%install
|
||||||
|
install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms
|
||||||
|
|
||||||
|
# Shell completions
|
||||||
|
install -d %{buildroot}%{_datadir}/bash-completion/completions
|
||||||
|
install -d %{buildroot}%{_datadir}/zsh/site-functions
|
||||||
|
install -d %{buildroot}%{_datadir}/fish/vendor_completions.d
|
||||||
|
%{_builddir}/dms-cli completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||||
|
%{_builddir}/dms-cli completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||||
|
%{_builddir}/dms-cli completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||||
|
|
||||||
|
install -Dm644 %{_builddir}/dms-qml/assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||||
|
|
||||||
|
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||||
|
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
|
||||||
|
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.git*
|
||||||
|
rm -f %{buildroot}%{_datadir}/quickshell/dms/.gitignore
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/.github
|
||||||
|
rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
|
||||||
|
|
||||||
|
%posttrans
|
||||||
|
# Clean up old installation path from previous versions (only if empty)
|
||||||
|
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
|
||||||
|
# Remove directories only if empty (preserves any user-added files)
|
||||||
|
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true
|
||||||
|
rmdir "%{_sysconfdir}/xdg/quickshell" 2>/dev/null || true
|
||||||
|
rmdir "%{_sysconfdir}/xdg" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
# Signal running DMS instances to reload (harmless if none running)
|
||||||
|
pkill -USR1 -x dms >/dev/null 2>&1 || :
|
||||||
|
|
||||||
|
%files
|
||||||
|
%license LICENSE
|
||||||
|
%doc README.md CONTRIBUTING.md
|
||||||
|
%{_datadir}/quickshell/dms/
|
||||||
|
%{_userunitdir}/dms.service
|
||||||
|
|
||||||
|
%files -n dms-cli
|
||||||
|
%{_bindir}/dms
|
||||||
|
%{_datadir}/bash-completion/completions/dms
|
||||||
|
%{_datadir}/zsh/site-functions/_dms
|
||||||
|
%{_datadir}/fish/vendor_completions.d/dms.fish
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-RELEASE_PLACEHOLDER
|
||||||
|
- Stable release VERSION_PLACEHOLDER
|
||||||
|
- Built from GitHub release
|
||||||
|
SPECEOF
|
||||||
|
|
||||||
|
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/dms.spec
|
||||||
|
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/dms.spec
|
||||||
|
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/dms.spec
|
||||||
|
|
||||||
|
echo "✅ Spec file generated for v${VERSION}-${RELEASE}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Spec file preview ==="
|
echo "=== Spec file preview ==="
|
||||||
head -40 ~/rpmbuild/SPECS/${PACKAGE}.spec
|
head -40 ~/rpmbuild/SPECS/dms.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 for ${PACKAGE}..."
|
echo "🔨 Building SRPM..."
|
||||||
rpmbuild -bs ${PACKAGE}.spec
|
rpmbuild -bs dms.spec
|
||||||
|
|
||||||
SRPM=$(ls ~/rpmbuild/SRPMS/${PACKAGE}-*.src.rpm | tail -n 1)
|
SRPM=$(ls ~/rpmbuild/SRPMS/*.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
|
||||||
@@ -136,7 +234,7 @@ jobs:
|
|||||||
- name: Upload SRPM artifact
|
- name: Upload SRPM artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.package }}-stable-srpm-${{ steps.version.outputs.version }}
|
name: dms-stable-srpm-${{ steps.version.outputs.version }}
|
||||||
path: ${{ steps.build.outputs.srpm_path }}
|
path: ${{ steps.build.outputs.srpm_path }}
|
||||||
retention-days: 90
|
retention-days: 90
|
||||||
|
|
||||||
@@ -157,40 +255,23 @@ 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 ${PACKAGE} SRPM to ${COPR_PROJECT}..."
|
echo "🚀 Uploading SRPM to avengemedia/dms..."
|
||||||
echo " SRPM: $(basename $SRPM)"
|
echo " SRPM: $(basename $SRPM)"
|
||||||
echo " Version: $VERSION"
|
echo " Version: $VERSION"
|
||||||
|
|
||||||
BUILD_OUTPUT=$(copr-cli build "$COPR_PROJECT" "$SRPM" --nowait 2>&1)
|
BUILD_OUTPUT=$(copr-cli build avengemedia/dms "$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/${COPR_PROJECT}/build/$BUILD_ID/"
|
echo "🔗 https://copr.fedorainfracloud.org/coprs/avengemedia/dms/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
|
||||||
@@ -198,13 +279,10 @@ jobs:
|
|||||||
- name: Build summary
|
- name: Build summary
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
PACKAGE="${{ matrix.package }}"
|
echo "### 🎉 DMS Stable Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
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/${COPR_PROJECT}/" >> $GITHUB_STEP_SUMMARY
|
echo "- **Project:** https://copr.fedorainfracloud.org/coprs/avengemedia/dms/" >> $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,14 +7,13 @@ 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
|
||||||
|
|
||||||
@@ -98,7 +97,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 (db$REBUILD)"
|
echo "🔄 Manual rebuild requested: $PKG (ppa$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
|
||||||
@@ -162,51 +161,16 @@ 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
|
||||||
# Manual workflow dispatch
|
# Use filtered packages from check-updates when package="all" and no rebuild requested
|
||||||
|
if [[ "${{ github.event.inputs.package }}" == "all" ]] && [[ -z "${{ github.event.inputs.rebuild_release }}" ]]; then
|
||||||
# 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
|
||||||
@@ -222,7 +186,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 "1.0.2")
|
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.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"
|
||||||
|
|
||||||
@@ -243,14 +207,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 "1.0.2")
|
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.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}db1) nightly; urgency=medium"
|
echo "dms-git ($NEW_VERSION) nightly; urgency=medium"
|
||||||
echo ""
|
echo ""
|
||||||
echo " * Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)"
|
echo " * Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -262,15 +226,10 @@ 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 files to version: $VERSION_NO_V"
|
echo "Updating packaging 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)
|
||||||
@@ -297,13 +256,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}db1) stable; urgency=medium"
|
echo "dms ($VERSION_NO_V) 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}db1"
|
echo "✓ Updated Debian changelog to $VERSION_NO_V"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
@@ -330,7 +289,6 @@ 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 }}"
|
||||||
|
|
||||||
@@ -342,7 +300,6 @@ 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)
|
||||||
@@ -352,7 +309,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: db$REBUILD_RELEASE"
|
echo "🔄 Using rebuild release number: ppa$REBUILD_RELEASE"
|
||||||
fi
|
fi
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
@@ -393,7 +350,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:** db${{ github.event.inputs.rebuild_release }}" >> $GITHUB_STEP_SUMMARY
|
echo "**Rebuild Number:** ppa${{ 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,8 +51,7 @@ jobs:
|
|||||||
check_stable_package() {
|
check_stable_package() {
|
||||||
local PKG="$1"
|
local PKG="$1"
|
||||||
local PPA_NAME="$2"
|
local PPA_NAME="$2"
|
||||||
# Use git ls-remote to find the latest tag, sorted by version (descending)
|
local LATEST_TAG=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | grep '"tag_name"' | sed 's/.*"tag_name": "v\?\([^"]*\)".*/\1/' || echo "")
|
||||||
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,12 +96,11 @@ 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,17 +1,6 @@
|
|||||||
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.dank-material-shell ];
|
imports = [ inputs.dms.homeModules.dankMaterialShell.default ];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=DMS
|
Name=DMS Application Picker
|
||||||
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;x-scheme-handler/dms;text/html;application/xhtml+xml;
|
MimeType=x-scheme-handler/http;x-scheme-handler/https;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=on-failure
|
Restart=always
|
||||||
RestartSec=1.23
|
RestartSec=1.23
|
||||||
TimeoutStopSec=10
|
TimeoutStopSec=10
|
||||||
|
|
||||||
|
|||||||
@@ -14,63 +14,34 @@ Distribution-aware installer with TUI for deploying DMS and compositor configura
|
|||||||
|
|
||||||
## System Integration
|
## System Integration
|
||||||
|
|
||||||
### Wayland Protocols (Client)
|
**Wayland Protocols**
|
||||||
|
- `wlr-gamma-control-unstable-v1` - Night mode and gamma control
|
||||||
|
- `wlr-screencopy-unstable-v1` - Screen capture for color picker
|
||||||
|
- `wlr-layer-shell-unstable-v1` - Overlay surfaces for color picker
|
||||||
|
- `wp-viewporter` - Fractional scaling support
|
||||||
|
- `dwl-ipc-unstable-v2` - dwl/MangoWC workspace integration
|
||||||
|
- `ext-workspace-v1` - Workspace protocol support
|
||||||
|
- `wlr-output-management-unstable-v1` - Display configuration
|
||||||
|
|
||||||
All Wayland protocols are consumed as a client - connecting to the compositor.
|
**DBus Interfaces**
|
||||||
|
- NetworkManager/iwd - Network management
|
||||||
|
- logind - Session control and inhibit locks
|
||||||
|
- accountsservice - User account information
|
||||||
|
- CUPS - Printer management
|
||||||
|
- Custom IPC via unix socket (JSON API)
|
||||||
|
|
||||||
| Protocol | Purpose |
|
**Hardware Control**
|
||||||
| ----------------------------------------- | ----------------------------------------------------------- |
|
- DDC/CI protocol - External monitor brightness control (like `ddcutil`)
|
||||||
| `wlr-gamma-control-unstable-v1` | Night mode color temperature control |
|
- Backlight control - Internal display brightness via `login1` or sysfs
|
||||||
| `wlr-screencopy-unstable-v1` | Screen capture for color picker/screenshot |
|
- LED control - Keyboard/device LED management
|
||||||
| `wlr-layer-shell-unstable-v1` | Overlay surfaces for color picker UI/screenshot |
|
- evdev input monitoring - Keyboard state tracking (caps lock, etc.)
|
||||||
| `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.)
|
||||||
@@ -99,7 +70,6 @@ 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
|
||||||
@@ -107,7 +77,6 @@ make test # Run tests
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Distribution build:**
|
**Distribution build:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make dist # Build without update/greeter features
|
make dist # Build without update/greeter features
|
||||||
```
|
```
|
||||||
@@ -115,7 +84,6 @@ 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
|
||||||
```
|
```
|
||||||
@@ -123,7 +91,6 @@ 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
|
||||||
```
|
```
|
||||||
@@ -131,7 +98,6 @@ 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 \
|
||||||
@@ -139,7 +105,6 @@ 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,6 +144,8 @@ var (
|
|||||||
clipConfigEnabled bool
|
clipConfigEnabled bool
|
||||||
clipConfigDisableHistory bool
|
clipConfigDisableHistory bool
|
||||||
clipConfigEnableHistory bool
|
clipConfigEnableHistory bool
|
||||||
|
clipConfigDisablePersist bool
|
||||||
|
clipConfigEnablePersist bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -171,6 +173,8 @@ 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)")
|
||||||
|
|
||||||
@@ -593,6 +597,12 @@ 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,6 +171,7 @@ 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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +220,7 @@ func getBaseVersion() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback
|
// Fallback
|
||||||
return "1.0.2"
|
return "0.6.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
func startDebugServer() error {
|
func startDebugServer() error {
|
||||||
@@ -513,6 +514,5 @@ func getCommonCommands() []*cobra.Command {
|
|||||||
notifyActionCmd,
|
notifyActionCmd,
|
||||||
matugenCmd,
|
matugenCmd,
|
||||||
clipboardCmd,
|
clipboardCmd,
|
||||||
doctorCmd,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ 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")
|
||||||
@@ -41,7 +40,6 @@ 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")
|
||||||
@@ -118,8 +116,6 @@ 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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,661 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/tui"
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/version"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var doctorCmd = &cobra.Command{
|
|
||||||
Use: "doctor",
|
|
||||||
Short: "Diagnose DMS installation and dependencies",
|
|
||||||
Long: "Check system health, verify dependencies, and diagnose configuration issues for DMS",
|
|
||||||
Run: runDoctor,
|
|
||||||
}
|
|
||||||
|
|
||||||
var doctorVerbose bool
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
doctorCmd.Flags().BoolVarP(&doctorVerbose, "verbose", "v", false, "Show detailed output including paths and versions")
|
|
||||||
}
|
|
||||||
|
|
||||||
type category int
|
|
||||||
|
|
||||||
const (
|
|
||||||
catSystem category = iota
|
|
||||||
catVersions
|
|
||||||
catInstallation
|
|
||||||
catCompositor
|
|
||||||
catQuickshellFeatures
|
|
||||||
catOptionalFeatures
|
|
||||||
catConfigFiles
|
|
||||||
catServices
|
|
||||||
)
|
|
||||||
|
|
||||||
var categoryNames = []string{
|
|
||||||
"System", "Versions", "Installation", "Compositor",
|
|
||||||
"Quickshell Features", "Optional Features", "Config Files", "Services",
|
|
||||||
}
|
|
||||||
|
|
||||||
type checkResult struct {
|
|
||||||
category category
|
|
||||||
name string
|
|
||||||
status string
|
|
||||||
message string
|
|
||||||
details string
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDoctor(cmd *cobra.Command, args []string) {
|
|
||||||
printDoctorHeader()
|
|
||||||
|
|
||||||
qsFeatures, qsMissingFeatures := checkQuickshellFeatures()
|
|
||||||
|
|
||||||
results := slices.Concat(
|
|
||||||
checkSystemInfo(),
|
|
||||||
checkVersions(qsMissingFeatures),
|
|
||||||
checkDMSInstallation(),
|
|
||||||
checkWindowManagers(),
|
|
||||||
qsFeatures,
|
|
||||||
checkOptionalDependencies(),
|
|
||||||
checkConfigurationFiles(),
|
|
||||||
checkSystemdServices(),
|
|
||||||
)
|
|
||||||
|
|
||||||
printResults(results)
|
|
||||||
printSummary(results, qsMissingFeatures)
|
|
||||||
}
|
|
||||||
|
|
||||||
func printDoctorHeader() {
|
|
||||||
theme := tui.TerminalTheme()
|
|
||||||
styles := tui.NewStyles(theme)
|
|
||||||
|
|
||||||
fmt.Println(getThemedASCII())
|
|
||||||
fmt.Println(styles.Title.Render("System Health Check"))
|
|
||||||
fmt.Println(styles.Subtle.Render("──────────────────────────────────────"))
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkSystemInfo() []checkResult {
|
|
||||||
results := []checkResult{}
|
|
||||||
|
|
||||||
osInfo, err := distros.GetOSInfo()
|
|
||||||
if err != nil {
|
|
||||||
status, message, details := "warn", fmt.Sprintf("Unknown (%v)", err), ""
|
|
||||||
|
|
||||||
if strings.Contains(err.Error(), "Unsupported distribution") {
|
|
||||||
osRelease := readOSRelease()
|
|
||||||
if osRelease["ID"] == "nixos" {
|
|
||||||
status = "ok"
|
|
||||||
message = osRelease["PRETTY_NAME"]
|
|
||||||
if message == "" {
|
|
||||||
message = fmt.Sprintf("NixOS %s", osRelease["VERSION_ID"])
|
|
||||||
}
|
|
||||||
details = "Supported for runtime (install via NixOS module or Flake)"
|
|
||||||
} else if osRelease["PRETTY_NAME"] != "" {
|
|
||||||
message = fmt.Sprintf("%s (not supported by dms setup)", osRelease["PRETTY_NAME"])
|
|
||||||
details = "DMS may work but automatic installation is not available"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results = append(results, checkResult{catSystem, "Operating System", status, message, details})
|
|
||||||
} else {
|
|
||||||
status := "ok"
|
|
||||||
message := osInfo.PrettyName
|
|
||||||
if message == "" {
|
|
||||||
message = fmt.Sprintf("%s %s", osInfo.Distribution.ID, osInfo.VersionID)
|
|
||||||
}
|
|
||||||
if distros.IsUnsupportedDistro(osInfo.Distribution.ID, osInfo.VersionID) {
|
|
||||||
status = "warn"
|
|
||||||
message += " (version may not be fully supported)"
|
|
||||||
}
|
|
||||||
results = append(results, checkResult{
|
|
||||||
catSystem, "Operating System", status, message,
|
|
||||||
fmt.Sprintf("ID: %s, Version: %s, Arch: %s", osInfo.Distribution.ID, osInfo.VersionID, osInfo.Architecture),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
arch := runtime.GOARCH
|
|
||||||
archStatus := "ok"
|
|
||||||
if arch != "amd64" && arch != "arm64" {
|
|
||||||
archStatus = "error"
|
|
||||||
}
|
|
||||||
results = append(results, checkResult{catSystem, "Architecture", archStatus, arch, ""})
|
|
||||||
|
|
||||||
waylandDisplay := os.Getenv("WAYLAND_DISPLAY")
|
|
||||||
xdgSessionType := os.Getenv("XDG_SESSION_TYPE")
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case waylandDisplay != "" || xdgSessionType == "wayland":
|
|
||||||
results = append(results, checkResult{
|
|
||||||
catSystem, "Display Server", "ok", "Wayland",
|
|
||||||
fmt.Sprintf("WAYLAND_DISPLAY=%s", waylandDisplay),
|
|
||||||
})
|
|
||||||
case xdgSessionType == "x11":
|
|
||||||
results = append(results, checkResult{catSystem, "Display Server", "error", "X11 (DMS requires Wayland)", ""})
|
|
||||||
default:
|
|
||||||
results = append(results, checkResult{
|
|
||||||
catSystem, "Display Server", "warn", "Unknown (ensure you're running Wayland)",
|
|
||||||
fmt.Sprintf("XDG_SESSION_TYPE=%s", xdgSessionType),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
func readOSRelease() map[string]string {
|
|
||||||
result := make(map[string]string)
|
|
||||||
data, err := os.ReadFile("/etc/os-release")
|
|
||||||
if err != nil {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
for line := range strings.SplitSeq(string(data), "\n") {
|
|
||||||
if parts := strings.SplitN(line, "=", 2); len(parts) == 2 {
|
|
||||||
result[parts[0]] = strings.Trim(parts[1], "\"")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkVersions(qsMissingFeatures bool) []checkResult {
|
|
||||||
results := []checkResult{
|
|
||||||
{catVersions, "DMS CLI", "info", formatVersion(Version), ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
qsVersion, qsStatus := getQuickshellVersionInfo(qsMissingFeatures)
|
|
||||||
results = append(results, checkResult{catVersions, "Quickshell", qsStatus, qsVersion, ""})
|
|
||||||
|
|
||||||
dmsVersion, dmsPath := getDMSShellVersion()
|
|
||||||
if dmsVersion != "" {
|
|
||||||
results = append(results, checkResult{catVersions, "DMS Shell", "ok", dmsVersion, dmsPath})
|
|
||||||
} else {
|
|
||||||
results = append(results, checkResult{catVersions, "DMS Shell", "error", "Not installed or not detected", "Run 'dms setup' to install"})
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDMSShellVersion() (version, path string) {
|
|
||||||
if err := findConfig(nil, nil); err == nil && configPath != "" {
|
|
||||||
versionFile := filepath.Join(configPath, "VERSION")
|
|
||||||
if data, err := os.ReadFile(versionFile); err == nil {
|
|
||||||
return strings.TrimSpace(string(data)), configPath
|
|
||||||
}
|
|
||||||
return "installed", configPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if dmsPath, err := config.LocateDMSConfig(); err == nil {
|
|
||||||
versionFile := filepath.Join(dmsPath, "VERSION")
|
|
||||||
if data, err := os.ReadFile(versionFile); err == nil {
|
|
||||||
return strings.TrimSpace(string(data)), dmsPath
|
|
||||||
}
|
|
||||||
return "installed", dmsPath
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func getQuickshellVersionInfo(missingFeatures bool) (string, string) {
|
|
||||||
if !utils.CommandExists("qs") {
|
|
||||||
return "Not installed", "error"
|
|
||||||
}
|
|
||||||
|
|
||||||
output, err := exec.Command("qs", "--version").Output()
|
|
||||||
if err != nil {
|
|
||||||
return "Installed (version check failed)", "warn"
|
|
||||||
}
|
|
||||||
|
|
||||||
fullVersion := strings.TrimSpace(string(output))
|
|
||||||
if matches := regexp.MustCompile(`quickshell (\d+\.\d+\.\d+)`).FindStringSubmatch(fullVersion); len(matches) >= 2 {
|
|
||||||
if version.CompareVersions(matches[1], "0.2.0") < 0 {
|
|
||||||
return fmt.Sprintf("%s (needs >= 0.2.0)", fullVersion), "error"
|
|
||||||
}
|
|
||||||
if missingFeatures {
|
|
||||||
return fullVersion, "warn"
|
|
||||||
}
|
|
||||||
return fullVersion, "ok"
|
|
||||||
}
|
|
||||||
|
|
||||||
return fullVersion, "warn"
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkDMSInstallation() []checkResult {
|
|
||||||
results := []checkResult{}
|
|
||||||
|
|
||||||
dmsPath := ""
|
|
||||||
if err := findConfig(nil, nil); err == nil && configPath != "" {
|
|
||||||
dmsPath = configPath
|
|
||||||
} else if path, err := config.LocateDMSConfig(); err == nil {
|
|
||||||
dmsPath = path
|
|
||||||
}
|
|
||||||
|
|
||||||
if dmsPath == "" {
|
|
||||||
return []checkResult{{catInstallation, "DMS Configuration", "error", "Not found", "shell.qml not found in any config path"}}
|
|
||||||
}
|
|
||||||
|
|
||||||
results = append(results, checkResult{catInstallation, "DMS Configuration", "ok", "Found", dmsPath})
|
|
||||||
|
|
||||||
shellQml := filepath.Join(dmsPath, "shell.qml")
|
|
||||||
if _, err := os.Stat(shellQml); err != nil {
|
|
||||||
results = append(results, checkResult{catInstallation, "shell.qml", "error", "Missing", shellQml})
|
|
||||||
} else {
|
|
||||||
results = append(results, checkResult{catInstallation, "shell.qml", "ok", "Present", shellQml})
|
|
||||||
}
|
|
||||||
|
|
||||||
if doctorVerbose {
|
|
||||||
installType := "Unknown"
|
|
||||||
switch {
|
|
||||||
case strings.Contains(dmsPath, "/nix/store"):
|
|
||||||
installType = "Nix store"
|
|
||||||
case strings.Contains(dmsPath, ".local/share") || strings.Contains(dmsPath, "/usr/share"):
|
|
||||||
installType = "System package"
|
|
||||||
case strings.Contains(dmsPath, ".config"):
|
|
||||||
installType = "User config"
|
|
||||||
}
|
|
||||||
results = append(results, checkResult{catInstallation, "Install Type", "info", installType, dmsPath})
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkWindowManagers() []checkResult {
|
|
||||||
compositors := []struct {
|
|
||||||
name, versionCmd, versionArg, versionRe string
|
|
||||||
commands []string
|
|
||||||
}{
|
|
||||||
{"Hyprland", "hyprctl", "version", `v?(\d+\.\d+\.\d+)`, []string{"hyprland", "Hyprland"}},
|
|
||||||
{"niri", "niri", "--version", `niri (\d+\.\d+)`, []string{"niri"}},
|
|
||||||
{"Sway", "sway", "--version", `sway version (\d+\.\d+)`, []string{"sway"}},
|
|
||||||
{"River", "river", "-version", `river (\d+\.\d+)`, []string{"river"}},
|
|
||||||
{"Wayfire", "wayfire", "--version", `wayfire (\d+\.\d+)`, []string{"wayfire"}},
|
|
||||||
}
|
|
||||||
|
|
||||||
results := []checkResult{}
|
|
||||||
foundAny := false
|
|
||||||
|
|
||||||
for _, c := range compositors {
|
|
||||||
if slices.ContainsFunc(c.commands, utils.CommandExists) {
|
|
||||||
foundAny = true
|
|
||||||
results = append(results, checkResult{
|
|
||||||
catCompositor, c.name, "ok",
|
|
||||||
getVersionFromCommand(c.versionCmd, c.versionArg, c.versionRe), "",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !foundAny {
|
|
||||||
results = append(results, checkResult{
|
|
||||||
catCompositor, "Compositor", "error",
|
|
||||||
"No supported Wayland compositor found",
|
|
||||||
"Install Hyprland, niri, Sway, River, or Wayfire",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if wm := detectRunningWM(); wm != "" {
|
|
||||||
results = append(results, checkResult{catCompositor, "Active", "info", wm, ""})
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
func getVersionFromCommand(cmd, arg, regex string) string {
|
|
||||||
output, err := exec.Command(cmd, arg).Output()
|
|
||||||
if err != nil {
|
|
||||||
return "installed"
|
|
||||||
}
|
|
||||||
|
|
||||||
outStr := string(output)
|
|
||||||
if matches := regexp.MustCompile(regex).FindStringSubmatch(outStr); len(matches) > 1 {
|
|
||||||
ver := matches[1]
|
|
||||||
if strings.Contains(outStr, "git") || strings.Contains(outStr, "dirty") {
|
|
||||||
return ver + " (git)"
|
|
||||||
}
|
|
||||||
return ver
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(outStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func detectRunningWM() string {
|
|
||||||
switch {
|
|
||||||
case os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") != "":
|
|
||||||
return "Hyprland"
|
|
||||||
case os.Getenv("NIRI_SOCKET") != "":
|
|
||||||
return "niri"
|
|
||||||
case os.Getenv("XDG_CURRENT_DESKTOP") != "":
|
|
||||||
return os.Getenv("XDG_CURRENT_DESKTOP")
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkQuickshellFeatures() ([]checkResult, bool) {
|
|
||||||
if !utils.CommandExists("qs") {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir := os.TempDir()
|
|
||||||
testScript := filepath.Join(tmpDir, "qs-feature-test.qml")
|
|
||||||
defer os.Remove(testScript)
|
|
||||||
|
|
||||||
qmlContent := `
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
ShellRoot {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool polkitAvailable: false
|
|
||||||
property bool idleMonitorAvailable: false
|
|
||||||
property bool idleInhibitorAvailable: false
|
|
||||||
property bool shortcutInhibitorAvailable: false
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
interval: 50
|
|
||||||
running: true
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
try {
|
|
||||||
var polkitTest = Qt.createQmlObject(
|
|
||||||
'import Quickshell.Services.Polkit; import QtQuick; Item {}',
|
|
||||||
root
|
|
||||||
)
|
|
||||||
root.polkitAvailable = true
|
|
||||||
polkitTest.destroy()
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var testItem = Qt.createQmlObject(
|
|
||||||
'import Quickshell.Wayland; import QtQuick; QtObject { ' +
|
|
||||||
'readonly property bool hasIdleMonitor: typeof IdleMonitor !== "undefined"; ' +
|
|
||||||
'readonly property bool hasIdleInhibitor: typeof IdleInhibitor !== "undefined"; ' +
|
|
||||||
'readonly property bool hasShortcutInhibitor: typeof ShortcutInhibitor !== "undefined" ' +
|
|
||||||
'}',
|
|
||||||
root
|
|
||||||
)
|
|
||||||
root.idleMonitorAvailable = testItem.hasIdleMonitor
|
|
||||||
root.idleInhibitorAvailable = testItem.hasIdleInhibitor
|
|
||||||
root.shortcutInhibitorAvailable = testItem.hasShortcutInhibitor
|
|
||||||
testItem.destroy()
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
console.warn(root.polkitAvailable ? "FEATURE:Polkit:OK" : "FEATURE:Polkit:UNAVAILABLE")
|
|
||||||
console.warn(root.idleMonitorAvailable ? "FEATURE:IdleMonitor:OK" : "FEATURE:IdleMonitor:UNAVAILABLE")
|
|
||||||
console.warn(root.idleInhibitorAvailable ? "FEATURE:IdleInhibitor:OK" : "FEATURE:IdleInhibitor:UNAVAILABLE")
|
|
||||||
console.warn(root.shortcutInhibitorAvailable ? "FEATURE:ShortcutInhibitor:OK" : "FEATURE:ShortcutInhibitor:UNAVAILABLE")
|
|
||||||
|
|
||||||
Quickshell.execDetached(["kill", "-TERM", String(Quickshell.processId)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
if err := os.WriteFile(testScript, []byte(qmlContent), 0644); err != nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command("qs", "-p", testScript)
|
|
||||||
cmd.Env = append(os.Environ(), "NO_COLOR=1")
|
|
||||||
output, _ := cmd.CombinedOutput()
|
|
||||||
outputStr := string(output)
|
|
||||||
|
|
||||||
features := []struct{ name, desc string }{
|
|
||||||
{"Polkit", "Authentication prompts"},
|
|
||||||
{"IdleMonitor", "Idle detection"},
|
|
||||||
{"IdleInhibitor", "Prevent idle/sleep"},
|
|
||||||
{"ShortcutInhibitor", "Allow shortcut management (niri)"},
|
|
||||||
}
|
|
||||||
|
|
||||||
results := []checkResult{}
|
|
||||||
missingFeatures := false
|
|
||||||
|
|
||||||
for _, f := range features {
|
|
||||||
available := strings.Contains(outputStr, fmt.Sprintf("FEATURE:%s:OK", f.name))
|
|
||||||
status, message := "ok", "Available"
|
|
||||||
if !available {
|
|
||||||
status, message = "info", "Not available"
|
|
||||||
missingFeatures = true
|
|
||||||
}
|
|
||||||
results = append(results, checkResult{catQuickshellFeatures, f.name, status, message, f.desc})
|
|
||||||
}
|
|
||||||
|
|
||||||
return results, missingFeatures
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkOptionalDependencies() []checkResult {
|
|
||||||
results := []checkResult{}
|
|
||||||
|
|
||||||
if utils.IsServiceActive("accounts-daemon", false) {
|
|
||||||
results = append(results, checkResult{catOptionalFeatures, "accountsservice", "ok", "Running", "User accounts"})
|
|
||||||
} else {
|
|
||||||
results = append(results, checkResult{catOptionalFeatures, "accountsservice", "warn", "Not running", "User accounts"})
|
|
||||||
}
|
|
||||||
|
|
||||||
terminals := []string{"ghostty", "kitty", "alacritty", "foot", "wezterm"}
|
|
||||||
terminalFound := ""
|
|
||||||
for _, term := range terminals {
|
|
||||||
if utils.CommandExists(term) {
|
|
||||||
terminalFound = term
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if terminalFound != "" {
|
|
||||||
results = append(results, checkResult{catOptionalFeatures, "Terminal", "ok", terminalFound, ""})
|
|
||||||
} else {
|
|
||||||
results = append(results, checkResult{catOptionalFeatures, "Terminal", "warn", "None found", "Install ghostty, kitty, or alacritty"})
|
|
||||||
}
|
|
||||||
|
|
||||||
deps := []struct {
|
|
||||||
name, cmd, altCmd, desc string
|
|
||||||
important bool
|
|
||||||
}{
|
|
||||||
{"matugen", "matugen", "", "Dynamic theming", true},
|
|
||||||
{"dgop", "dgop", "", "System monitoring", true},
|
|
||||||
{"cava", "cava", "", "Audio waveform", false},
|
|
||||||
{"khal", "khal", "", "Calendar events", false},
|
|
||||||
{"Network", "nmcli", "iwctl", "Network management", false},
|
|
||||||
{"danksearch", "dsearch", "", "File search", false},
|
|
||||||
{"loginctl", "loginctl", "", "Session management", false},
|
|
||||||
{"fprintd", "fprintd-list", "", "Fingerprint auth", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, d := range deps {
|
|
||||||
found, foundCmd := utils.CommandExists(d.cmd), d.cmd
|
|
||||||
if !found && d.altCmd != "" {
|
|
||||||
if utils.CommandExists(d.altCmd) {
|
|
||||||
found, foundCmd = true, d.altCmd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if found {
|
|
||||||
message := "Installed"
|
|
||||||
switch foundCmd {
|
|
||||||
case "nmcli":
|
|
||||||
message = "NetworkManager"
|
|
||||||
case "iwctl":
|
|
||||||
message = "iwd"
|
|
||||||
}
|
|
||||||
results = append(results, checkResult{catOptionalFeatures, d.name, "ok", message, d.desc})
|
|
||||||
} else if d.important {
|
|
||||||
results = append(results, checkResult{catOptionalFeatures, d.name, "warn", "Missing", d.desc})
|
|
||||||
} else {
|
|
||||||
results = append(results, checkResult{catOptionalFeatures, d.name, "info", "Not installed", d.desc})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkConfigurationFiles() []checkResult {
|
|
||||||
configFiles := []struct{ name, path string }{
|
|
||||||
{"Settings", filepath.Join(utils.XDGConfigHome(), "DankMaterialShell", "settings.json")},
|
|
||||||
{"Session", filepath.Join(utils.XDGStateHome(), "DankMaterialShell", "session.json")},
|
|
||||||
{"Colors", filepath.Join(utils.XDGCacheHome(), "DankMaterialShell", "dms-colors.json")},
|
|
||||||
}
|
|
||||||
|
|
||||||
results := []checkResult{}
|
|
||||||
for _, cf := range configFiles {
|
|
||||||
if _, err := os.Stat(cf.path); err == nil {
|
|
||||||
results = append(results, checkResult{catConfigFiles, cf.name, "ok", "Present", cf.path})
|
|
||||||
} else {
|
|
||||||
results = append(results, checkResult{catConfigFiles, cf.name, "info", "Not yet created", cf.path})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkSystemdServices() []checkResult {
|
|
||||||
if !utils.CommandExists("systemctl") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
results := []checkResult{}
|
|
||||||
|
|
||||||
dmsState := getServiceState("dms", true)
|
|
||||||
if !dmsState.exists {
|
|
||||||
results = append(results, checkResult{catServices, "dms.service", "info", "Not installed", "Optional user service"})
|
|
||||||
} else {
|
|
||||||
status, message := "ok", dmsState.enabled
|
|
||||||
if dmsState.active != "" {
|
|
||||||
message = fmt.Sprintf("%s, %s", dmsState.enabled, dmsState.active)
|
|
||||||
}
|
|
||||||
if dmsState.enabled == "disabled" {
|
|
||||||
status, message = "warn", "Disabled"
|
|
||||||
}
|
|
||||||
results = append(results, checkResult{catServices, "dms.service", status, message, ""})
|
|
||||||
}
|
|
||||||
|
|
||||||
greetdState := getServiceState("greetd", false)
|
|
||||||
if greetdState.exists {
|
|
||||||
status := "ok"
|
|
||||||
if greetdState.enabled == "disabled" {
|
|
||||||
status = "info"
|
|
||||||
}
|
|
||||||
results = append(results, checkResult{catServices, "greetd", status, greetdState.enabled, ""})
|
|
||||||
} else if doctorVerbose {
|
|
||||||
results = append(results, checkResult{catServices, "greetd", "info", "Not installed", "Optional greeter service"})
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
type serviceState struct {
|
|
||||||
exists bool
|
|
||||||
enabled string
|
|
||||||
active string
|
|
||||||
}
|
|
||||||
|
|
||||||
func getServiceState(name string, userService bool) serviceState {
|
|
||||||
args := []string{"is-enabled", name}
|
|
||||||
if userService {
|
|
||||||
args = []string{"--user", "is-enabled", name}
|
|
||||||
}
|
|
||||||
|
|
||||||
output, _ := exec.Command("systemctl", args...).Output()
|
|
||||||
enabled := strings.TrimSpace(string(output))
|
|
||||||
|
|
||||||
if enabled == "" || enabled == "not-found" {
|
|
||||||
return serviceState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
state := serviceState{exists: true, enabled: enabled}
|
|
||||||
|
|
||||||
if userService {
|
|
||||||
output, _ = exec.Command("systemctl", "--user", "is-active", name).Output()
|
|
||||||
if active := strings.TrimSpace(string(output)); active != "" && active != "unknown" {
|
|
||||||
state.active = active
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func printResults(results []checkResult) {
|
|
||||||
theme := tui.TerminalTheme()
|
|
||||||
styles := tui.NewStyles(theme)
|
|
||||||
|
|
||||||
currentCategory := category(-1)
|
|
||||||
for _, r := range results {
|
|
||||||
if r.category != currentCategory {
|
|
||||||
if currentCategory != -1 {
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
fmt.Printf(" %s\n", styles.Bold.Render(categoryNames[r.category]))
|
|
||||||
currentCategory = r.category
|
|
||||||
}
|
|
||||||
printResultLine(r, styles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printResultLine(r checkResult, styles tui.Styles) {
|
|
||||||
icon, style := "○", styles.Subtle
|
|
||||||
switch r.status {
|
|
||||||
case "ok":
|
|
||||||
icon, style = "●", styles.Success
|
|
||||||
case "warn":
|
|
||||||
icon, style = "●", styles.Warning
|
|
||||||
case "error":
|
|
||||||
icon, style = "●", styles.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
name := r.name
|
|
||||||
if len(name) > 18 {
|
|
||||||
name = name[:17] + "…"
|
|
||||||
}
|
|
||||||
dots := strings.Repeat("·", 19-len(name))
|
|
||||||
|
|
||||||
fmt.Printf(" %s %s %s %s\n", style.Render(icon), name, styles.Subtle.Render(dots), r.message)
|
|
||||||
|
|
||||||
if doctorVerbose && r.details != "" {
|
|
||||||
fmt.Printf(" %s\n", styles.Subtle.Render("└─ "+r.details))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printSummary(results []checkResult, qsMissingFeatures bool) {
|
|
||||||
theme := tui.TerminalTheme()
|
|
||||||
styles := tui.NewStyles(theme)
|
|
||||||
|
|
||||||
errors, warnings, ok := 0, 0, 0
|
|
||||||
for _, r := range results {
|
|
||||||
switch r.status {
|
|
||||||
case "error":
|
|
||||||
errors++
|
|
||||||
case "warn":
|
|
||||||
warnings++
|
|
||||||
case "ok":
|
|
||||||
ok++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Printf(" %s\n", styles.Subtle.Render("──────────────────────────────────────"))
|
|
||||||
|
|
||||||
if errors == 0 && warnings == 0 {
|
|
||||||
fmt.Printf(" %s\n", styles.Success.Render("✓ All checks passed!"))
|
|
||||||
} else {
|
|
||||||
parts := []string{}
|
|
||||||
if errors > 0 {
|
|
||||||
parts = append(parts, styles.Error.Render(fmt.Sprintf("%d error(s)", errors)))
|
|
||||||
}
|
|
||||||
if warnings > 0 {
|
|
||||||
parts = append(parts, styles.Warning.Render(fmt.Sprintf("%d warning(s)", warnings)))
|
|
||||||
}
|
|
||||||
parts = append(parts, styles.Success.Render(fmt.Sprintf("%d ok", ok)))
|
|
||||||
fmt.Printf(" %s\n", strings.Join(parts, ", "))
|
|
||||||
|
|
||||||
if qsMissingFeatures {
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Printf(" %s\n", styles.Subtle.Render("→ Consider using quickshell-git for full feature support"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
@@ -131,12 +131,6 @@ 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
|
||||||
@@ -183,7 +177,7 @@ func runOpen(target string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
method := "apppicker.open"
|
method := "apppicker.open"
|
||||||
if detectedMimeType == "" && len(detectedCategories) == 0 && (strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") || strings.HasPrefix(target, "dms://")) {
|
if detectedMimeType == "" && len(detectedCategories) == 0 && (strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://")) {
|
||||||
method = "browser.open"
|
method = "browser.open"
|
||||||
params["url"] = target
|
params["url"] = target
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,25 +18,6 @@ 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) {
|
||||||
@@ -199,16 +180,6 @@ 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
|
||||||
@@ -243,28 +214,14 @@ func runShellInteractive(session bool) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case sig := <-sigChan:
|
case sig := <-sigChan:
|
||||||
if sig == syscall.SIGUSR1 {
|
// Handle SIGUSR1 restart for non-session managed processes
|
||||||
if isSessionManaged {
|
if sig == syscall.SIGUSR1 && !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)
|
||||||
@@ -278,7 +235,7 @@ func runShellInteractive(session bool) {
|
|||||||
cmd.Process.Signal(syscall.SIGTERM)
|
cmd.Process.Signal(syscall.SIGTERM)
|
||||||
}
|
}
|
||||||
os.Remove(socketPath)
|
os.Remove(socketPath)
|
||||||
os.Exit(getProcessExitCode(cmd.ProcessState))
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -443,16 +400,6 @@ 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)
|
||||||
@@ -493,28 +440,15 @@ func runShellDaemon(session bool) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case sig := <-sigChan:
|
case sig := <-sigChan:
|
||||||
if sig == syscall.SIGUSR1 {
|
// Handle SIGUSR1 restart for non-session managed processes
|
||||||
if isSessionManaged {
|
if sig == syscall.SIGUSR1 && !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)
|
// All other signals: clean shutdown
|
||||||
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)
|
||||||
@@ -526,7 +460,7 @@ func runShellDaemon(session bool) {
|
|||||||
cmd.Process.Signal(syscall.SIGTERM)
|
cmd.Process.Signal(syscall.SIGTERM)
|
||||||
}
|
}
|
||||||
os.Remove(socketPath)
|
os.Remove(socketPath)
|
||||||
os.Exit(getProcessExitCode(cmd.ProcessState))
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,11 +213,6 @@ 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)
|
||||||
}
|
}
|
||||||
@@ -270,13 +265,7 @@ func (cd *ConfigDeployer) deployGhosttyConfig() ([]DeploymentResult, error) {
|
|||||||
|
|
||||||
colorResult := DeploymentResult{
|
colorResult := DeploymentResult{
|
||||||
ConfigType: "Ghostty Colors",
|
ConfigType: "Ghostty Colors",
|
||||||
Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "themes", "dankcolors"),
|
Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config-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, "theme = dankcolors")
|
assert.Contains(t, GhosttyConfig, "config-file = ./config-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
|
||||||
theme = dankcolors
|
config-file = ./config-dankcolors
|
||||||
|
|||||||
@@ -112,24 +112,3 @@ 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,7 +550,10 @@ func (b *BaseDistribution) WriteEnvironmentConfig(terminal deps.Terminal) error
|
|||||||
terminalCmd = "ghostty"
|
terminalCmd = "ghostty"
|
||||||
}
|
}
|
||||||
|
|
||||||
content := fmt.Sprintf(`ELECTRON_OZONE_PLATFORM_HINT=auto
|
content := fmt.Sprintf(`QT_QPA_PLATFORM=wayland
|
||||||
|
ELECTRON_OZONE_PLATFORM_HINT=auto
|
||||||
|
QT_QPA_PLATFORMTHEME=gtk3
|
||||||
|
QT_QPA_PLATFORMTHEME_QT6=gtk3
|
||||||
TERMINAL=%s
|
TERMINAL=%s
|
||||||
`, terminalCmd)
|
`, terminalCmd)
|
||||||
|
|
||||||
@@ -564,6 +567,12 @@ 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,7 +4,6 @@ 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"
|
||||||
@@ -385,8 +384,6 @@ 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 {
|
||||||
@@ -430,7 +427,7 @@ func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Packa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add repository
|
// Add repository
|
||||||
repoLine := fmt.Sprintf("deb [signed-by=%s, arch=%s] %s/ /", keyringPath, runtime.GOARCH, baseURL)
|
repoLine := fmt.Sprintf("deb [signed-by=%s] %s/ /", keyringPath, baseURL)
|
||||||
|
|
||||||
progressChan <- InstallProgressMsg{
|
progressChan <- InstallProgressMsg{
|
||||||
Phase: PhaseSystemPackages,
|
Phase: PhaseSystemPackages,
|
||||||
|
|||||||
@@ -15,12 +15,6 @@ 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 {
|
||||||
@@ -440,19 +434,6 @@ 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))
|
||||||
@@ -460,8 +441,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/%s/%s.repo",
|
repoURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/openSUSE_Tumbleweed/%s.repo",
|
||||||
repoPath, obsDistroVersion, pkg.RepoURL)
|
repoPath, 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,12 +19,11 @@ 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
|
||||||
VersionCodename string
|
PrettyName string
|
||||||
PrettyName string
|
Architecture string
|
||||||
Architecture string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOSInfo detects the current OS and returns information about it
|
// GetOSInfo detects the current OS and returns information about it
|
||||||
@@ -73,8 +72,6 @@ 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
|
||||||
}
|
}
|
||||||
@@ -103,10 +100,6 @@ 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,9 +258,7 @@ 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")
|
||||||
}
|
}
|
||||||
@@ -276,9 +274,6 @@ 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")
|
||||||
|
|||||||
@@ -208,6 +208,9 @@ 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,6 +319,10 @@ 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()
|
||||||
}
|
}
|
||||||
@@ -344,6 +348,105 @@ 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")
|
||||||
@@ -392,9 +495,6 @@ 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() {
|
||||||
@@ -1209,7 +1309,13 @@ func (m *Manager) applyConfigChange(newCfg Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Clipboard config reloaded: disableHistory=%v", newCfg.DisableHistory)
|
if newCfg.DisablePersist && !oldCfg.DisablePersist {
|
||||||
|
log.Info("Clipboard persist disabled, releasing ownership")
|
||||||
|
m.releaseOwnership()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Clipboard config reloaded: disableHistory=%v disablePersist=%v",
|
||||||
|
newCfg.DisableHistory, newCfg.DisablePersist)
|
||||||
|
|
||||||
m.updateState()
|
m.updateState()
|
||||||
m.notifySubscribers()
|
m.notifySubscribers()
|
||||||
|
|||||||
@@ -458,6 +458,7 @@ 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,6 +21,7 @@ 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 {
|
||||||
@@ -29,6 +30,7 @@ func DefaultConfig() Config {
|
|||||||
MaxEntrySize: 5 * 1024 * 1024,
|
MaxEntrySize: 5 * 1024 * 1024,
|
||||||
AutoClearDays: 0,
|
AutoClearDays: 0,
|
||||||
ClearAtStartup: false,
|
ClearAtStartup: false,
|
||||||
|
DisablePersist: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package cups
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -158,42 +156,9 @@ 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 {
|
||||||
devices, err := m.pkHelper.DevicesGet(10, 0, nil, nil)
|
return 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()
|
||||||
@@ -211,9 +176,6 @@ 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,7 +42,6 @@ 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 {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func (b *NetworkManagerBackend) ListVPNProfiles() ([]VPNProfile, error) {
|
|||||||
return nil, fmt.Errorf("failed to get connections: %w", err)
|
return nil, fmt.Errorf("failed to get connections: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
profiles := []VPNProfile{}
|
var profiles []VPNProfile
|
||||||
for _, conn := range connections {
|
for _, conn := range connections {
|
||||||
settings, err := conn.GetSettings()
|
settings, err := conn.GetSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -101,7 +101,7 @@ func (b *NetworkManagerBackend) ListActiveVPN() ([]VPNActive, error) {
|
|||||||
return nil, fmt.Errorf("failed to get active connections: %w", err)
|
return nil, fmt.Errorf("failed to get active connections: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
active := []VPNActive{}
|
var active []VPNActive
|
||||||
for _, activeConn := range activeConns {
|
for _, activeConn := range activeConns {
|
||||||
connType, err := activeConn.GetPropertyType()
|
connType, err := activeConn.GetPropertyType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ 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"
|
||||||
)
|
)
|
||||||
@@ -38,11 +37,6 @@ 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")
|
||||||
@@ -210,6 +204,9 @@ 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())
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
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),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
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")
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
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"`
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
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),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
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),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -241,7 +241,6 @@ 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()
|
||||||
})
|
})
|
||||||
@@ -361,23 +360,22 @@ 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,
|
||||||
AdaptiveSyncSupported: head.adaptiveSyncSupported,
|
ID: head.id,
|
||||||
ID: head.id,
|
|
||||||
}
|
}
|
||||||
outputs = append(outputs, output)
|
outputs = append(outputs, output)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -17,23 +17,22 @@ 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"`
|
||||||
AdaptiveSyncSupported bool `json:"adaptiveSyncSupported"`
|
ID uint32 `json:"id"`
|
||||||
ID uint32 `json:"id"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
@@ -73,26 +72,25 @@ 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
|
||||||
adaptiveSyncSupported bool
|
finished bool
|
||||||
finished bool
|
ready bool
|
||||||
ready bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type modeState struct {
|
type modeState struct {
|
||||||
@@ -171,7 +169,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 || oldOut.AdaptiveSyncSupported != newOut.AdaptiveSyncSupported {
|
if oldOut.AdaptiveSync != newOut.AdaptiveSync {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (oldOut.CurrentMode == nil) != (newOut.CurrentMode == nil) {
|
if (oldOut.CurrentMode == nil) != (newOut.CurrentMode == nil) {
|
||||||
|
|||||||
@@ -1,258 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
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,24 +1,8 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import "os/exec"
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CommandExists(cmd string) bool {
|
func CommandExists(cmd string) bool {
|
||||||
_, err := exec.LookPath(cmd)
|
_, err := exec.LookPath(cmd)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsServiceActive(name string, userService bool) bool {
|
|
||||||
if !CommandExists("systemctl") {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{"is-active", name}
|
|
||||||
if userService {
|
|
||||||
args = []string{"--user", "is-active", name}
|
|
||||||
}
|
|
||||||
output, _ := exec.Command("systemctl", args...).Output()
|
|
||||||
return strings.TrimSpace(string(output)) == "active"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,30 +6,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func xdgDir(envVar string, defaultPath ...string) string {
|
|
||||||
if dir := os.Getenv(envVar); dir != "" {
|
|
||||||
return dir
|
|
||||||
}
|
|
||||||
home, _ := os.UserHomeDir()
|
|
||||||
return filepath.Join(append([]string{home}, defaultPath...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func XDGConfigHome() string {
|
|
||||||
return xdgDir("XDG_CONFIG_HOME", ".config")
|
|
||||||
}
|
|
||||||
|
|
||||||
func XDGStateHome() string {
|
|
||||||
return xdgDir("XDG_STATE_HOME", ".local", "state")
|
|
||||||
}
|
|
||||||
|
|
||||||
func XDGCacheHome() string {
|
|
||||||
return xdgDir("XDG_CACHE_HOME", ".cache")
|
|
||||||
}
|
|
||||||
|
|
||||||
func XDGDataHome() string {
|
|
||||||
return xdgDir("XDG_DATA_HOME", ".local", "share")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExpandPath(path string) (string, error) {
|
func ExpandPath(path string) (string, error) {
|
||||||
expanded := os.ExpandEnv(path)
|
expanded := os.ExpandEnv(path)
|
||||||
expanded = filepath.Clean(expanded)
|
expanded = filepath.Clean(expanded)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
dms-git (1.0.2+git2528.d336866fdb1) nightly; urgency=medium
|
dms-git (1.0.2+git2528.d336866f) nightly; urgency=medium
|
||||||
|
|
||||||
* Git snapshot (commit 2528: d336866f)
|
* Git snapshot (commit 2528: d336866f)
|
||||||
|
|
||||||
@@ -16,6 +16,23 @@ 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.3.tar.gz</param>
|
<param name="path">/AvengeMedia/DankMaterialShell/archive/refs/tags/v1.0.2.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.3/dms-distropkg-amd64.gz</param>
|
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.0.2/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.3/dms-distropkg-arm64.gz</param>
|
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.0.2/dms-distropkg-arm64.gz</param>
|
||||||
</service>
|
</service>
|
||||||
</services>
|
</services>
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
dms (1.0.3db1) unstable; urgency=medium
|
dms (1.0.2ppa6) unstable; urgency=medium
|
||||||
|
|
||||||
* Update to v1.0.3 stable release
|
* Rebuild to fix repository metadata issues
|
||||||
|
|
||||||
-- 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 | quickshell-git,
|
quickshell-git | quickshell,
|
||||||
accountsservice,
|
accountsservice,
|
||||||
cava,
|
cava,
|
||||||
cliphist,
|
cliphist,
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
# 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 - Stable releases
|
# Spec for DMS Greeter - Git builds using rpkg macros
|
||||||
|
|
||||||
%global debug_package %{nil}
|
%global debug_package %{nil}
|
||||||
%global version VERSION_PLACEHOLDER
|
%global version {{{ git_repo_version }}}
|
||||||
%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: RELEASE_PLACEHOLDER%{?dist}
|
Release: 0.git%{?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 }}}
|
||||||
|
|
||||||
Source0: dms-qml.tar.gz
|
BuildRequires: git-core
|
||||||
|
# For the _tmpfilesdir macro.
|
||||||
BuildRequires: gzip
|
BuildRequires: systemd-rpm-macros
|
||||||
BuildRequires: wget
|
|
||||||
BuildRequires: systemd-rpm-macros
|
|
||||||
|
|
||||||
Requires: greetd
|
Requires: greetd
|
||||||
Requires: (quickshell-git or quickshell)
|
Requires: (quickshell-git or quickshell)
|
||||||
@@ -24,11 +24,14 @@ Requires(post): /usr/sbin/useradd
|
|||||||
Requires(post): /usr/sbin/groupadd
|
Requires(post): /usr/sbin/groupadd
|
||||||
|
|
||||||
Recommends: policycoreutils-python-utils
|
Recommends: policycoreutils-python-utils
|
||||||
Recommends: acl
|
Recommends: setfacl
|
||||||
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.
|
||||||
@@ -38,26 +41,31 @@ compositor detection and configuration. Features session selection, user
|
|||||||
authentication, and dynamic theming.
|
authentication, and dynamic theming.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%setup -q -c -n dms-qml
|
{{{ git_repo_setup_macro }}}
|
||||||
|
|
||||||
%build
|
|
||||||
|
|
||||||
%install
|
%install
|
||||||
# Install greeter files to shared data location
|
# Install greeter files to shared data location (from quickshell/ subdirectory)
|
||||||
install -dm755 %{buildroot}%{_datadir}/quickshell/dms-greeter
|
install -dm755 %{buildroot}%{_datadir}/quickshell/dms-greeter
|
||||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms-greeter/
|
cp -r quickshell/* %{buildroot}%{_datadir}/quickshell/dms-greeter/
|
||||||
|
|
||||||
install -Dm755 %{_builddir}/dms-qml/Modules/Greetd/assets/dms-greeter %{buildroot}%{_bindir}/dms-greeter
|
# Install launcher script
|
||||||
|
install -Dm755 quickshell/Modules/Greetd/assets/dms-greeter %{buildroot}%{_bindir}/dms-greeter
|
||||||
|
|
||||||
install -Dm644 %{_builddir}/dms-qml/Modules/Greetd/README.md %{buildroot}%{_docdir}/dms-greeter/README.md
|
# Install documentation
|
||||||
|
install -Dm644 quickshell/Modules/Greetd/README.md %{buildroot}%{_docdir}/dms-greeter/README.md
|
||||||
|
|
||||||
install -Dpm0644 %{_builddir}/dms-qml/systemd/tmpfiles-dms-greeter.conf %{buildroot}%{_tmpfilesdir}/dms-greeter.conf
|
# Create cache directory for greeter data
|
||||||
|
install -Dpm0644 quickshell/systemd/tmpfiles-dms-greeter.conf %{buildroot}%{_tmpfilesdir}/dms-greeter.conf
|
||||||
|
|
||||||
install -Dm644 %{_builddir}/dms-qml/LICENSE %{buildroot}%{_docdir}/dms-greeter/LICENSE
|
# Install LICENSE file
|
||||||
|
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 w/greetd packages
|
# Note: We do NOT install a PAM config here to avoid conflicting with greetd package
|
||||||
|
# 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
|
||||||
@@ -65,8 +73,9 @@ 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 & preserves any user-added files
|
# Remove directories only if empty (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
|
||||||
@@ -80,7 +89,7 @@ fi
|
|||||||
%{_tmpfilesdir}/%{name}.conf
|
%{_tmpfilesdir}/%{name}.conf
|
||||||
|
|
||||||
%pre
|
%pre
|
||||||
# Create greeter user/group if they don't exist
|
# Create greeter user/group if they don't exist (greetd expects this)
|
||||||
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 \
|
||||||
@@ -118,6 +127,7 @@ 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
|
||||||
@@ -139,6 +149,7 @@ 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
|
||||||
@@ -187,8 +198,9 @@ 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
|
||||||
|
|
||||||
@@ -255,6 +267,4 @@ if [ "$1" -eq 0 ] && [ -x /usr/sbin/semanage ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-RELEASE_PLACEHOLDER
|
{{{ git_repo_changelog }}}
|
||||||
- Stable release VERSION_PLACEHOLDER
|
|
||||||
- Built from GitHub release
|
|
||||||
|
|||||||
@@ -1,40 +1,49 @@
|
|||||||
# Feodra spec for DMS stable releases
|
# Spec for DMS - uses rpkg macros for git builds
|
||||||
|
|
||||||
%global debug_package %{nil}
|
%global debug_package %{nil}
|
||||||
%global version VERSION_PLACEHOLDER
|
%global version {{{ git_repo_version }}}
|
||||||
%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: RELEASE_PLACEHOLDER%{?dist}
|
Release: 1%{?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 }}}
|
||||||
|
|
||||||
Source0: dms-qml.tar.gz
|
BuildRequires: git-core
|
||||||
|
|
||||||
BuildRequires: gzip
|
BuildRequires: gzip
|
||||||
|
BuildRequires: golang >= 1.24
|
||||||
|
BuildRequires: make
|
||||||
BuildRequires: wget
|
BuildRequires: wget
|
||||||
BuildRequires: systemd-rpm-macros
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
Requires: (quickshell or quickshell-git)
|
# Core requirements
|
||||||
|
Requires: (quickshell-git or quickshell)
|
||||||
Requires: accountsservice
|
Requires: accountsservice
|
||||||
Requires: dms-cli = %{version}-%{release}
|
Requires: dms-cli = %{epoch}:%{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 and hyprland compositors. Features notifications,
|
and optimized for the niri, hyprland, sway, and dwl (MangoWC) 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,
|
||||||
@@ -51,14 +60,24 @@ 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
|
||||||
%setup -q -c -n dms-qml
|
{{{ 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
|
case "%{_arch}" in
|
||||||
x86_64)
|
x86_64)
|
||||||
ARCH_SUFFIX="amd64"
|
DMS_BINARY="dms-linux-amd64"
|
||||||
;;
|
;;
|
||||||
aarch64)
|
aarch64)
|
||||||
ARCH_SUFFIX="arm64"
|
DMS_BINARY="dms-linux-arm64"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unsupported architecture: %{_arch}"
|
echo "Unsupported architecture: %{_arch}"
|
||||||
@@ -66,35 +85,27 @@ case "%{_arch}" in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Download dms-cli for target architecture
|
install -Dm755 core/bin/${DMS_BINARY} %{buildroot}%{_bindir}/dms
|
||||||
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
|
||||||
%{_builddir}/dms-cli completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
core/bin/${DMS_BINARY} completion bash > %{buildroot}%{_datadir}/bash-completion/completions/dms || :
|
||||||
%{_builddir}/dms-cli completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
core/bin/${DMS_BINARY} completion zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
|
||||||
%{_builddir}/dms-cli completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
core/bin/${DMS_BINARY} completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || :
|
||||||
|
|
||||||
install -Dm644 %{_builddir}/dms-qml/assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
# Install systemd user service
|
||||||
|
install -Dm644 assets/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
|
||||||
|
|
||||||
install -Dm644 %{_builddir}/dms-qml/assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop
|
install -Dm644 assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop
|
||||||
install -Dm644 %{_builddir}/dms-qml/assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
|
install -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
|
install -dm755 %{buildroot}%{_datadir}/quickshell/dms
|
||||||
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
|
cp -r quickshell/* %{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
|
||||||
@@ -108,7 +119,8 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
|
|||||||
|
|
||||||
%files
|
%files
|
||||||
%license LICENSE
|
%license LICENSE
|
||||||
%doc README.md CONTRIBUTING.md
|
%doc 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
|
||||||
@@ -121,6 +133,4 @@ pkill -USR1 -x dms >/dev/null 2>&1 || :
|
|||||||
%{_datadir}/fish/vendor_completions.d/dms.fish
|
%{_datadir}/fish/vendor_completions.d/dms.fish
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-RELEASE_PLACEHOLDER
|
{{{ git_repo_changelog }}}
|
||||||
- Stable release VERSION_PLACEHOLDER
|
|
||||||
- Built from GitHub release
|
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.programs.dank-material-shell;
|
cfg = config.programs.dankMaterialShell;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages = [
|
packages = [
|
||||||
dmsPkgs.dms-shell
|
dmsPkgs.dms-shell
|
||||||
]
|
]
|
||||||
++ lib.optional cfg.enableSystemMonitoring cfg.dgop.package
|
++ lib.optional cfg.enableSystemMonitoring dmsPkgs.dgop
|
||||||
++ lib.optionals cfg.enableVPN [
|
++ lib.optionals cfg.enableVPN [
|
||||||
pkgs.glib
|
pkgs.glib
|
||||||
pkgs.networkmanager
|
pkgs.networkmanager
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
{ 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.dank-material-shell.greeter;
|
cfg = config.programs.dankMaterialShell.greeter;
|
||||||
|
|
||||||
inherit (config.services.greetd.settings.default_session) user;
|
inherit (config.services.greetd.settings.default_session) user;
|
||||||
|
|
||||||
@@ -44,20 +44,19 @@ in
|
|||||||
{
|
{
|
||||||
imports =
|
imports =
|
||||||
let
|
let
|
||||||
msg = "The option 'programs.dank-material-shell.greeter.compositor.extraConfig' is deprecated. Please use 'programs.dank-material-shell.greeter.compositor.customConfig' instead.";
|
msg = "The option 'programs.dankMaterialShell.greeter.compositor.extraConfig' is deprecated. Please use 'programs.dankMaterialShell.greeter.compositor.customConfig' instead.";
|
||||||
in
|
in
|
||||||
[
|
[
|
||||||
(lib.mkRemovedOptionModule [
|
(lib.mkRemovedOptionModule [
|
||||||
"programs"
|
"programs"
|
||||||
"dank-material-shell"
|
"dankMaterialShell"
|
||||||
"greeter"
|
"greeter"
|
||||||
"compositor"
|
"compositor"
|
||||||
"extraConfig"
|
"extraConfig"
|
||||||
] msg)
|
] msg)
|
||||||
./dms-rename.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
options.programs.dank-material-shell.greeter = {
|
options.programs.dankMaterialShell.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 [
|
||||||
@@ -178,7 +177,7 @@ in
|
|||||||
mv dms-colors.json colors.json || :
|
mv dms-colors.json colors.json || :
|
||||||
chown ${user}: * || :
|
chown ${user}: * || :
|
||||||
'';
|
'';
|
||||||
programs.dank-material-shell.greeter.configFiles = lib.mkIf (cfg.configHome != null) [
|
programs.dankMaterialShell.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.dank-material-shell;
|
cfg = config.programs.dankMaterialShell;
|
||||||
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"
|
||||||
"dank-material-shell"
|
"dankMaterialShell"
|
||||||
"enableNightMode"
|
"enableNightMode"
|
||||||
] "Night mode is now always available.")
|
] "Night mode is now always available.")
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "programs" "dank-material-shell" "enableSystemd" ]
|
[ "programs" "dankMaterialShell" "enableSystemd" ]
|
||||||
[ "programs" "dank-material-shell" "systemd" "enable" ]
|
[ "programs" "dankMaterialShell" "systemd" "enable" ]
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
options.programs.dank-material-shell = with lib.types; {
|
options.programs.dankMaterialShell = with lib.types; {
|
||||||
default = {
|
default = {
|
||||||
settings = lib.mkOption {
|
settings = lib.mkOption {
|
||||||
type = jsonFormat.type;
|
type = jsonFormat.type;
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.programs.dank-material-shell;
|
cfg = config.programs.dankMaterialShell;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
options.programs.dankMaterialShell = {
|
||||||
./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.dank-material-shell;
|
cfg = config.programs.dankMaterialShell;
|
||||||
common = import ./common.nix {
|
common = import ./common.nix {
|
||||||
inherit
|
inherit
|
||||||
config
|
config
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
dmsPkgs,
|
dmsPkgs,
|
||||||
pkgs,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
inherit (lib) types;
|
inherit (lib) types;
|
||||||
path = [
|
path = [
|
||||||
"programs"
|
"programs"
|
||||||
"dank-material-shell"
|
"dankMaterialShell"
|
||||||
];
|
];
|
||||||
|
|
||||||
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.";
|
||||||
@@ -21,58 +20,46 @@ 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.dank-material-shell = {
|
options.programs.dankMaterialShell = {
|
||||||
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 dank-material-shell changes";
|
description = "Auto-restart dms.service when dankMaterialShell changes";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
dgop = {
|
|
||||||
package = lib.mkPackageOption pkgs "dgop";
|
|
||||||
};
|
|
||||||
|
|
||||||
enableSystemMonitoring = lib.mkOption {
|
enableSystemMonitoring = lib.mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = "Add needed dependencies to use system monitoring widgets";
|
description = "Add needed dependencies to use system monitoring widgets";
|
||||||
};
|
};
|
||||||
|
|
||||||
enableVPN = lib.mkOption {
|
enableVPN = lib.mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = "Add needed dependencies to use the VPN widget";
|
description = "Add needed dependencies to use the VPN widget";
|
||||||
};
|
};
|
||||||
|
|
||||||
enableDynamicTheming = lib.mkOption {
|
enableDynamicTheming = lib.mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = "Add needed dependencies to have dynamic theming support";
|
description = "Add needed dependencies to have dynamic theming support";
|
||||||
};
|
};
|
||||||
|
|
||||||
enableAudioWavelength = lib.mkOption {
|
enableAudioWavelength = lib.mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = "Add needed dependencies to have audio wavelength support";
|
description = "Add needed dependencies to have audio wavelength support";
|
||||||
};
|
};
|
||||||
|
|
||||||
enableCalendarEvents = lib.mkOption {
|
enableCalendarEvents = lib.mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = "Add calendar events support via khal";
|
description = "Add calendar events support via khal";
|
||||||
};
|
};
|
||||||
|
|
||||||
quickshell = {
|
quickshell = {
|
||||||
package = lib.mkPackageOption dmsPkgs "quickshell" {
|
package = lib.mkPackageOption dmsPkgs "quickshell" {
|
||||||
extraDescription = "The quickshell package to use (defaults to be built from source, due to unreleased features used by DMS).";
|
extraDescription = "The quickshell package to use (defaults to be built from source, in the commit 26531f due to unreleased features used by DMS).";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
%global debug_package %{nil}
|
%global debug_package %{nil}
|
||||||
|
|
||||||
Name: dms
|
Name: dms
|
||||||
Version: 1.0.3
|
Version: 1.0.2
|
||||||
Release: 1%{?dist}
|
Release: 7%{?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 or quickshell-git)
|
Requires: (quickshell-git or quickshell)
|
||||||
Requires: accountsservice
|
Requires: accountsservice
|
||||||
Requires: dgop
|
Requires: dgop
|
||||||
|
|
||||||
@@ -105,9 +105,6 @@ 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,121 +2,226 @@
|
|||||||
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: ./copr-upload.sh [PACKAGE] [VERSION] [RELEASE]
|
# Usage: ./create-upload-copr.sh VERSION [RELEASE]
|
||||||
# Examples:
|
# Example: ./create-upload-copr.sh 1.0.0 4
|
||||||
# ./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)"
|
||||||
|
|
||||||
# Determine Copr project based on package
|
echo "Building DMS v${VERSION}-${RELEASE} SRPM for Copr..."
|
||||||
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
|
||||||
|
|
||||||
# Download source tarball from GitHub releases
|
# Create the corrected QML tarball locally
|
||||||
echo "📦 Downloading source tarball for v${VERSION}..."
|
echo "Creating QML tarball with assets..."
|
||||||
if [ ! -f ~/rpmbuild/SOURCES/dms-qml.tar.gz ]; then
|
TEMP_DIR=$(mktemp -d)
|
||||||
wget -O ~/rpmbuild/SOURCES/dms-qml.tar.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/download/v${VERSION}/dms-qml.tar.gz" || {
|
cd "$REPO_ROOT"
|
||||||
echo "❌ Failed to download dms-qml.tar.gz for v${VERSION}"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
echo "✅ Source tarball downloaded"
|
|
||||||
else
|
|
||||||
echo "✅ Source tarball already exists"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy and prepare spec file
|
# Copy quickshell contents to temp
|
||||||
echo "📝 Preparing spec file..."
|
cp -r quickshell/* "$TEMP_DIR/"
|
||||||
SPEC_FILE="$REPO_ROOT/distro/fedora/${PACKAGE}.spec"
|
|
||||||
if [ ! -f "$SPEC_FILE" ]; then
|
|
||||||
echo "❌ Spec file not found: $SPEC_FILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cp "$SPEC_FILE" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
# Copy root LICENSE and CONTRIBUTING.md
|
||||||
|
cp LICENSE CONTRIBUTING.md "$TEMP_DIR/"
|
||||||
|
|
||||||
# Replace placeholders in spec file
|
# 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')"
|
CHANGELOG_DATE="$(date '+%a %b %d %Y')"
|
||||||
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
|
||||||
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
|
||||||
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/"${PACKAGE}".spec
|
|
||||||
|
|
||||||
echo "✅ Spec file prepared for ${PACKAGE} v${VERSION}-${RELEASE}"
|
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
|
||||||
|
# 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 locally with corrected tarball
|
||||||
|
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
|
||||||
|
|
||||||
# Build SRPM
|
# Build SRPM
|
||||||
echo "🔨 Building SRPM..."
|
echo "Building SRPM..."
|
||||||
cd ~/rpmbuild/SPECS
|
cd ~/rpmbuild/SPECS
|
||||||
rpmbuild -bs "${PACKAGE}".spec
|
rpmbuild -bs dms.spec
|
||||||
|
|
||||||
SRPM=$(ls ~/rpmbuild/SRPMS/"${PACKAGE}"-"${VERSION}"-*.src.rpm | tail -n 1)
|
SRPM=$(ls ~/rpmbuild/SRPMS/dms-"${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 $COPR_PROJECT $SRPM"
|
echo "Upload manually with: copr-cli build avengemedia/dms $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 "$COPR_PROJECT" "$SRPM" --nowait; then
|
if copr-cli build avengemedia/dms "$SRPM" --nowait; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "✅ Build submitted successfully!"
|
echo "Build submitted successfully! Check status at:"
|
||||||
echo "📊 Check status at:"
|
echo "https://copr.fedorainfracloud.org/coprs/avengemedia/dms/builds/"
|
||||||
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 $COPR_PROJECT $SRPM"
|
echo " copr-cli build avengemedia/dms $SRPM"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Or upload via web interface:"
|
echo "Or upload via web interface:"
|
||||||
echo " https://copr.fedorainfracloud.org/coprs/${COPR_PROJECT}/builds/"
|
echo " https://copr.fedorainfracloud.org/coprs/avengemedia/dms/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 db2 suffix
|
# ./distro/scripts/obs-upload.sh debian dms-git 2 # Rebuild with ppa2 suffix
|
||||||
# ./distro/scripts/obs-upload.sh dms-git --rebuild=2 # Rebuild with db2 suffix (flag syntax)
|
# ./distro/scripts/obs-upload.sh dms-git --rebuild=2 # Rebuild with ppa2 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})(db[0-9]+)?$' | grep -oP '[a-f0-9]{8}' || echo "")
|
OBS_COMMIT=$(echo "$OBS_VERSION" | grep -oP '\.([a-f0-9]{8})(ppa[0-9]+)?$' | grep -oP '[a-f0-9]{8}' || echo "")
|
||||||
NEW_COMMIT=$(echo "$VERSION" | grep -oP '\.([a-f0-9]{8})(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 "")
|
||||||
|
|
||||||
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,8 +279,7 @@ 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
|
||||||
BASE_VERSION=$(echo "$CHANGELOG_VERSION" | sed 's/db[0-9]*$//')
|
CHANGELOG_VERSION="${CHANGELOG_VERSION}ppa${REBUILD_RELEASE}"
|
||||||
CHANGELOG_VERSION="${BASE_VERSION}db${REBUILD_RELEASE}"
|
|
||||||
echo " - Applied rebuild suffix: $CHANGELOG_VERSION"
|
echo " - Applied rebuild suffix: $CHANGELOG_VERSION"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -308,16 +307,12 @@ 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 "==> Version $CHANGELOG_VERSION already exists in OBS"
|
echo "==> Error: Version $CHANGELOG_VERSION already exists in OBS"
|
||||||
echo " This exact version (including db${REBUILD_RELEASE}) is already uploaded."
|
echo " This exact version (including ppa${REBUILD_RELEASE}) is already uploaded."
|
||||||
echo " Skipping upload - nothing to do."
|
echo " To rebuild with a different release number, try incrementing:"
|
||||||
echo ""
|
|
||||||
echo " 💡 To rebuild with a different release number, try incrementing:"
|
|
||||||
NEXT_NUM=$((REBUILD_RELEASE + 1))
|
NEXT_NUM=$((REBUILD_RELEASE + 1))
|
||||||
echo " REBUILD_RELEASE=$NEXT_NUM"
|
echo " ./distro/scripts/obs-upload.sh $PACKAGE $NEXT_NUM"
|
||||||
echo ""
|
exit 1
|
||||||
echo "✓ Exiting gracefully (no changes needed)"
|
|
||||||
exit 0
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -516,7 +511,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
|
||||||
@@ -539,17 +534,9 @@ 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 "Attempted both wget and curl"
|
echo "Tried both wget and curl. Please check the URL and network connectivity."
|
||||||
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
|
||||||
@@ -566,7 +553,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
|
||||||
@@ -725,10 +712,6 @@ 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)
|
||||||
@@ -740,10 +723,6 @@ 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
|
||||||
|
|
||||||
@@ -817,29 +796,23 @@ EOF
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "==> Ensuring we're in the OSC working directory"
|
cd "$WORK_DIR"
|
||||||
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_CURRENT=""
|
KEEP_PATTERN=""
|
||||||
if [[ -n "$CHANGELOG_VERSION" ]]; then
|
if [[ -n "$CHANGELOG_VERSION" ]]; then
|
||||||
KEEP_CURRENT="${PACKAGE}_${CHANGELOG_VERSION}.tar.gz"
|
BASE_KEEP_VERSION=$(echo "$CHANGELOG_VERSION" | sed 's/ppa[0-9]*$//')
|
||||||
echo " Keeping only current version: ${KEEP_CURRENT}"
|
KEEP_PATTERN="${PACKAGE}_${BASE_KEEP_VERSION}"
|
||||||
|
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 [[ "$old_file" == "$KEEP_CURRENT" ]]; then
|
if [[ -n "$KEEP_PATTERN" ]] && [[ "$old_file" == ${KEEP_PATTERN}* ]]; then
|
||||||
echo " - Keeping: $old_file"
|
echo " - Keeping current version: $old_file"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -862,11 +835,14 @@ else
|
|||||||
echo " ⚠️ Could not fetch file list from server, skipping cleanup"
|
echo " ⚠️ Could not fetch file list from server, skipping cleanup"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update working copy to latest revision (without expanding service files to avoid revision conflicts)
|
# Fallback update with --server-side-source-service-files flag only syncs metadata (spec, dsc, _service)
|
||||||
echo "==> Updating working copy"
|
echo "==> Updating working copy"
|
||||||
if ! osc up 2>/dev/null; then
|
if ! osc up --server-side-source-service-files 2>/dev/null; then
|
||||||
echo "Error: Failed to update working copy"
|
echo " Note: Using regular update (--server-side-source-service-files not supported)"
|
||||||
exit 1
|
if ! osc up; then
|
||||||
|
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
|
||||||
@@ -906,15 +882,6 @@ 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"
|
||||||
@@ -941,7 +908,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 --skip-local-service-run -m "$MESSAGE" 2>&1 | grep -v "Git SCM package" | grep -v "apiurl\|project\|_ObsPrj\|_manifest\|git-obs"
|
osc commit -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,8 +191,19 @@ fi
|
|||||||
cd "$WORK_PACKAGE_DIR"
|
cd "$WORK_PACKAGE_DIR"
|
||||||
get_latest_tag() {
|
get_latest_tag() {
|
||||||
local repo="$1"
|
local repo="$1"
|
||||||
# Get the latest tag, sorted by version
|
if command -v curl &>/dev/null; then
|
||||||
git ls-remote --tags --refs --sort='-v:refname' "https://github.com/$repo.git" | head -n1 | awk -F/ '{print $NF}' | sed 's/^v//'
|
LATEST_TAG=$(curl -s "https://api.github.com/repos/$repo/releases/latest" 2>/dev/null | grep '"tag_name":' | sed 's/.*"tag_name": "\(.*\)".*/\1/' | head -1)
|
||||||
|
if [ -n "$LATEST_TAG" ]; then
|
||||||
|
echo "$LATEST_TAG" | sed 's/^v//'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
TEMP_REPO=$(mktemp -d "$TEMP_BASE/ppa_tag_XXXXXX")
|
||||||
|
if git clone --depth=1 --quiet "https://github.com/$repo.git" "$TEMP_REPO" 2>/dev/null; then
|
||||||
|
LATEST_TAG=$(cd "$TEMP_REPO" && git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "")
|
||||||
|
rm -rf "$TEMP_REPO"
|
||||||
|
echo "$LATEST_TAG"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
IS_GIT_PACKAGE=false
|
IS_GIT_PACKAGE=false
|
||||||
@@ -323,17 +334,6 @@ 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,10 +341,6 @@ 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))
|
||||||
|
|||||||
5
distro/ubuntu/danklinux/danksearch/debian/changelog
Normal file
5
distro/ubuntu/danklinux/danksearch/debian/changelog
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
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
|
||||||
24
distro/ubuntu/danklinux/danksearch/debian/control
Normal file
24
distro/ubuntu/danklinux/danksearch/debian/control
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
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
|
||||||
24
distro/ubuntu/danklinux/danksearch/debian/copyright
Normal file
24
distro/ubuntu/danklinux/danksearch/debian/copyright
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
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".
|
||||||
33
distro/ubuntu/danklinux/danksearch/debian/rules
Executable file
33
distro/ubuntu/danklinux/danksearch/debian/rules
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/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
distro/ubuntu/danklinux/danksearch/debian/source/format
Normal file
1
distro/ubuntu/danklinux/danksearch/debian/source/format
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
BIN
distro/ubuntu/danklinux/danksearch/dsearch-amd64
Executable file
BIN
distro/ubuntu/danklinux/danksearch/dsearch-amd64
Executable file
Binary file not shown.
BIN
distro/ubuntu/danklinux/danksearch/dsearch-arm64
Executable file
BIN
distro/ubuntu/danklinux/danksearch/dsearch-arm64
Executable file
Binary file not shown.
9
distro/ubuntu/danklinux/dgop/debian/changelog
Normal file
9
distro/ubuntu/danklinux/dgop/debian/changelog
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
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
|
||||||
27
distro/ubuntu/danklinux/dgop/debian/control
Normal file
27
distro/ubuntu/danklinux/dgop/debian/control
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
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
|
||||||
27
distro/ubuntu/danklinux/dgop/debian/copyright
Normal file
27
distro/ubuntu/danklinux/dgop/debian/copyright
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
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.
|
||||||
38
distro/ubuntu/danklinux/dgop/debian/rules
Executable file
38
distro/ubuntu/danklinux/dgop/debian/rules
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/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
distro/ubuntu/danklinux/dgop/debian/source/format
Normal file
1
distro/ubuntu/danklinux/dgop/debian/source/format
Normal file
@@ -0,0 +1 @@
|
|||||||
|
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: any
|
Architecture: amd64
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
quickshell-git | quickshell,
|
quickshell-git | quickshell,
|
||||||
accountsservice,
|
accountsservice,
|
||||||
|
|||||||
@@ -32,17 +32,15 @@ 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 ARCH=$(DEB_HOST_ARCH)"; \
|
echo "Building with VERSION=$$VERSION COMMIT=$$COMMIT"; \
|
||||||
cd dms-git-repo/core && $(MAKE) GOFLAGS="-mod=vendor" dist ARCH=$(DEB_HOST_ARCH) VERSION="$$VERSION" COMMIT="$$COMMIT"; \
|
cd dms-git-repo/core && $(MAKE) GOFLAGS="-mod=vendor" dist ARCH=amd64 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=$(DEB_HOST_ARCH); \
|
cd dms-git-repo/core && $(MAKE) GOFLAGS="-mod=vendor" dist ARCH=amd64; \
|
||||||
fi
|
fi
|
||||||
cp dms-git-repo/core/bin/dms-linux-$(DEB_HOST_ARCH) dms
|
cp dms-git-repo/core/bin/dms-linux-amd64 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: any
|
Architecture: all
|
||||||
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: any
|
Architecture: amd64
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
quickshell | quickshell-git,
|
quickshell-git | quickshell,
|
||||||
accountsservice,
|
accountsservice,
|
||||||
cava,
|
cava,
|
||||||
cliphist,
|
cliphist,
|
||||||
|
|||||||
@@ -19,14 +19,11 @@ 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
|
||||||
$(eval DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH))
|
test -f dms-distropkg-amd64.gz || (echo "ERROR: dms-distropkg-amd64.gz not found!" && exit 1)
|
||||||
@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-$(DEB_HOST_ARCH).gz > dms
|
gunzip -c dms-distropkg-amd64.gz > dms
|
||||||
chmod +x dms
|
chmod +x dms
|
||||||
|
|
||||||
# Extract source tarball for QML files
|
# Extract source tarball for QML files
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"dark": {
|
|
||||||
"name": "Gruvbox Material Dark Soft",
|
|
||||||
"surface": "#32302f",
|
|
||||||
"surfaceText": "#fbf1c7",
|
|
||||||
"surfaceVariant": "#3c3836",
|
|
||||||
"surfaceVariantText": "#ebdbb2",
|
|
||||||
"background": "#3c3836",
|
|
||||||
"backgroundText": "#ddc7a1",
|
|
||||||
"outline": "#7c6f64",
|
|
||||||
"surfaceContainer": "#3c3836",
|
|
||||||
"surfaceContainerHigh": "#5a524c",
|
|
||||||
"surfaceContainerHighest": "#7c6f64",
|
|
||||||
"primary": "#a9b665",
|
|
||||||
"secondary": "#d8a657",
|
|
||||||
"primaryText": "#292828",
|
|
||||||
"primaryContainer": "#798247",
|
|
||||||
"surfaceTint": "#7daea3",
|
|
||||||
"error": "#ea6962",
|
|
||||||
"warning": "#d3869b",
|
|
||||||
"info": "#89b482",
|
|
||||||
"matugen_type": "scheme-tonal-spot"
|
|
||||||
},
|
|
||||||
"light": {
|
|
||||||
"name": "Gruvbox Material Light Soft",
|
|
||||||
"surface": "#f2e5bc",
|
|
||||||
"surfaceText": "#282828",
|
|
||||||
"surfaceVariant": "#ebdbb2",
|
|
||||||
"surfaceVariantText": "#3c3836",
|
|
||||||
"background": "#ebdbb2",
|
|
||||||
"backgroundText": "#654735",
|
|
||||||
"outline": "#a89984",
|
|
||||||
"surfaceContainer": "#ebdbb2",
|
|
||||||
"surfaceContainerHigh": "#c9b99a",
|
|
||||||
"surfaceContainerHighest": "#a89984",
|
|
||||||
"primary": "#6c782e",
|
|
||||||
"secondary": "#b47109",
|
|
||||||
"primaryText": "#292828",
|
|
||||||
"primaryContainer": "#535d23",
|
|
||||||
"surfaceTint": "#45707a",
|
|
||||||
"error": "#c14a4a",
|
|
||||||
"warning": "#945e80",
|
|
||||||
"info": "#6c782e",
|
|
||||||
"matugen_type": "scheme-tonal-spot"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
37
flake.lock
generated
37
flake.lock
generated
@@ -1,12 +1,32 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"dgop": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1762835999,
|
||||||
|
"narHash": "sha256-UykYGrGFOFTmDpKTLNxj1wvd1gbDG4TkqLNSbV0TYwk=",
|
||||||
|
"owner": "AvengeMedia",
|
||||||
|
"repo": "dgop",
|
||||||
|
"rev": "799301991cd5dcea9b64245f9d500dcc76615653",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "AvengeMedia",
|
||||||
|
"repo": "dgop",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766651565,
|
"lastModified": 1764950072,
|
||||||
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
|
"narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
|
"rev": "f61125a668a320878494449750330ca58b78c557",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -23,22 +43,23 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766725085,
|
"lastModified": 1764663772,
|
||||||
"narHash": "sha256-O2aMFdDUYJazFrlwL7aSIHbUSEm3ADVZjmf41uBJfHs=",
|
"narHash": "sha256-sHqLmm0wAt3PC4vczJeBozI1/f4rv9yp3IjkClHDXDs=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "41828c4180fb921df7992a5405f5ff05d2ac2fff",
|
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460",
|
||||||
"revCount": 715,
|
"revCount": 713,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"rev": "41828c4180fb921df7992a5405f5ff05d2ac2fff",
|
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
"url": "https://git.outfoxxed.me/quickshell/quickshell"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"dgop": "dgop",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"quickshell": "quickshell"
|
"quickshell": "quickshell"
|
||||||
}
|
}
|
||||||
|
|||||||
30
flake.nix
30
flake.nix
@@ -3,8 +3,12 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
dgop = {
|
||||||
|
url = "github:AvengeMedia/dgop";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
quickshell = {
|
quickshell = {
|
||||||
url = "git+https://git.outfoxxed.me/quickshell/quickshell?rev=41828c4180fb921df7992a5405f5ff05d2ac2fff";
|
url = "git+https://git.outfoxxed.me/quickshell/quickshell?rev=26531fc46ef17e9365b03770edd3fb9206fcb460";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -13,6 +17,7 @@
|
|||||||
{
|
{
|
||||||
self,
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
|
dgop,
|
||||||
quickshell,
|
quickshell,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
@@ -24,14 +29,15 @@
|
|||||||
);
|
);
|
||||||
buildDmsPkgs = pkgs: {
|
buildDmsPkgs = pkgs: {
|
||||||
dms-shell = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
dms-shell = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
||||||
|
inherit (dgop.packages.${pkgs.stdenv.hostPlatform.system}) dgop;
|
||||||
quickshell = quickshell.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
quickshell = quickshell.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
||||||
};
|
};
|
||||||
mkModuleWithDmsPkgs =
|
mkModuleWithDmsPkgs =
|
||||||
modulePath:
|
path:
|
||||||
args@{ pkgs, ... }:
|
args@{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(import modulePath (args // { dmsPkgs = buildDmsPkgs pkgs; }))
|
(import path (args // { dmsPkgs = buildDmsPkgs pkgs; }))
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
mkQmlImportPath =
|
mkQmlImportPath =
|
||||||
@@ -139,30 +145,18 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
quickshell = quickshell.packages.${system}.default;
|
|
||||||
|
|
||||||
default = self.packages.${system}.dms-shell;
|
default = self.packages.${system}.dms-shell;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
homeModules.dank-material-shell = mkModuleWithDmsPkgs ./distro/nix/home.nix;
|
homeModules.dankMaterialShell.default = mkModuleWithDmsPkgs ./distro/nix/home.nix;
|
||||||
|
|
||||||
homeModules.default = self.homeModules.dank-material-shell;
|
homeModules.dankMaterialShell.niri = import ./distro/nix/niri.nix;
|
||||||
|
|
||||||
homeModules.niri = import ./distro/nix/niri.nix;
|
nixosModules.dankMaterialShell = mkModuleWithDmsPkgs ./distro/nix/nixos.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,9 +16,6 @@ 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,9 +8,6 @@ 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) {
|
||||||
@@ -20,17 +17,5 @@ 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
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
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: 4
|
readonly property int settingsConfigVersion: 3
|
||||||
|
|
||||||
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,9 +63,7 @@ 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: ""
|
||||||
@@ -74,8 +72,6 @@ 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
|
||||||
@@ -109,12 +105,9 @@ 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
|
||||||
@@ -172,13 +165,11 @@ Singleton {
|
|||||||
property int maxWorkspaceIcons: 3
|
property int maxWorkspaceIcons: 3
|
||||||
property bool workspacesPerMonitor: true
|
property bool workspacesPerMonitor: true
|
||||||
property bool showOccupiedWorkspacesOnly: false
|
property bool showOccupiedWorkspacesOnly: false
|
||||||
property bool reverseScrolling: false
|
|
||||||
property bool dwlShowAllTags: false
|
property bool dwlShowAllTags: false
|
||||||
property var workspaceNameIcons: ({})
|
property var workspaceNameIcons: ({})
|
||||||
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
|
||||||
@@ -264,7 +255,6 @@ 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
|
||||||
@@ -289,11 +279,9 @@ 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
|
||||||
@@ -314,7 +302,6 @@ 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
|
||||||
@@ -373,279 +360,50 @@ 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,
|
||||||
"showOnWindowsOpen": false,
|
openOnOverview: false,
|
||||||
"openOnOverview": false,
|
visible: true,
|
||||||
"visible": true,
|
popupGapsAuto: true,
|
||||||
"popupGapsAuto": true,
|
popupGapsManual: 4,
|
||||||
"popupGapsManual": 4,
|
maximizeDetection: true,
|
||||||
"maximizeDetection": true,
|
scrollEnabled: true,
|
||||||
"scrollEnabled": true,
|
scrollXBehavior: "column",
|
||||||
"scrollXBehavior": "column",
|
scrollYBehavior: "workspace"
|
||||||
"scrollYBehavior": "workspace"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
property bool desktopClockEnabled: false
|
|
||||||
property string desktopClockStyle: "analog"
|
|
||||||
property real desktopClockTransparency: 0.8
|
|
||||||
property string desktopClockColorMode: "primary"
|
|
||||||
property color desktopClockCustomColor: "#ffffff"
|
|
||||||
property bool desktopClockShowDate: true
|
|
||||||
property bool desktopClockShowAnalogNumbers: false
|
|
||||||
property bool desktopClockShowAnalogSeconds: true
|
|
||||||
property real desktopClockX: -1
|
|
||||||
property real desktopClockY: -1
|
|
||||||
property real desktopClockWidth: 280
|
|
||||||
property real desktopClockHeight: 180
|
|
||||||
property var desktopClockDisplayPreferences: ["all"]
|
|
||||||
|
|
||||||
property bool systemMonitorEnabled: false
|
|
||||||
property bool systemMonitorShowHeader: true
|
|
||||||
property real systemMonitorTransparency: 0.8
|
|
||||||
property string systemMonitorColorMode: "primary"
|
|
||||||
property color systemMonitorCustomColor: "#ffffff"
|
|
||||||
property bool systemMonitorShowCpu: true
|
|
||||||
property bool systemMonitorShowCpuGraph: true
|
|
||||||
property bool systemMonitorShowCpuTemp: true
|
|
||||||
property bool systemMonitorShowGpuTemp: false
|
|
||||||
property string systemMonitorGpuPciId: ""
|
|
||||||
property bool systemMonitorShowMemory: true
|
|
||||||
property bool systemMonitorShowMemoryGraph: true
|
|
||||||
property bool systemMonitorShowNetwork: true
|
|
||||||
property bool systemMonitorShowNetworkGraph: true
|
|
||||||
property bool systemMonitorShowDisk: true
|
|
||||||
property bool systemMonitorShowTopProcesses: false
|
|
||||||
property int systemMonitorTopProcessCount: 3
|
|
||||||
property string systemMonitorTopProcessSortBy: "cpu"
|
|
||||||
property string systemMonitorLayoutMode: "auto"
|
|
||||||
property int systemMonitorGraphInterval: 60
|
|
||||||
property real systemMonitorX: -1
|
|
||||||
property real systemMonitorY: -1
|
|
||||||
property real systemMonitorWidth: 320
|
|
||||||
property real systemMonitorHeight: 480
|
|
||||||
property var systemMonitorDisplayPreferences: ["all"]
|
|
||||||
property var systemMonitorVariants: []
|
|
||||||
property var desktopWidgetPositions: ({})
|
|
||||||
property var desktopWidgetGridSettings: ({})
|
|
||||||
property var desktopWidgetInstances: []
|
|
||||||
|
|
||||||
function getDesktopWidgetGridSetting(screenKey, property, defaultValue) {
|
|
||||||
const val = desktopWidgetGridSettings?.[screenKey]?.[property];
|
|
||||||
return val !== undefined ? val : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDesktopWidgetGridSetting(screenKey, property, value) {
|
|
||||||
const allSettings = JSON.parse(JSON.stringify(desktopWidgetGridSettings || {}));
|
|
||||||
if (!allSettings[screenKey])
|
|
||||||
allSettings[screenKey] = {};
|
|
||||||
allSettings[screenKey][property] = value;
|
|
||||||
desktopWidgetGridSettings = allSettings;
|
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDesktopWidgetPosition(pluginId, screenKey, property, defaultValue) {
|
|
||||||
const pos = desktopWidgetPositions?.[pluginId]?.[screenKey]?.[property];
|
|
||||||
return pos !== undefined ? pos : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateDesktopWidgetPosition(pluginId, screenKey, updates) {
|
|
||||||
const allPositions = JSON.parse(JSON.stringify(desktopWidgetPositions || {}));
|
|
||||||
if (!allPositions[pluginId])
|
|
||||||
allPositions[pluginId] = {};
|
|
||||||
allPositions[pluginId][screenKey] = Object.assign({}, allPositions[pluginId][screenKey] || {}, updates);
|
|
||||||
desktopWidgetPositions = allPositions;
|
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSystemMonitorVariants() {
|
|
||||||
return systemMonitorVariants || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function createSystemMonitorVariant(name, config) {
|
|
||||||
const id = "sysmon_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
|
||||||
const variant = {
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
config: config || getDefaultSystemMonitorConfig()
|
|
||||||
};
|
|
||||||
const variants = JSON.parse(JSON.stringify(systemMonitorVariants || []));
|
|
||||||
variants.push(variant);
|
|
||||||
systemMonitorVariants = variants;
|
|
||||||
saveSettings();
|
|
||||||
return variant;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSystemMonitorVariant(variantId, updates) {
|
|
||||||
const variants = JSON.parse(JSON.stringify(systemMonitorVariants || []));
|
|
||||||
const idx = variants.findIndex(v => v.id === variantId);
|
|
||||||
if (idx === -1)
|
|
||||||
return;
|
|
||||||
Object.assign(variants[idx], updates);
|
|
||||||
systemMonitorVariants = variants;
|
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeSystemMonitorVariant(variantId) {
|
|
||||||
const variants = (systemMonitorVariants || []).filter(v => v.id !== variantId);
|
|
||||||
systemMonitorVariants = variants;
|
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSystemMonitorVariant(variantId) {
|
|
||||||
return (systemMonitorVariants || []).find(v => v.id === variantId) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDefaultSystemMonitorConfig() {
|
|
||||||
return {
|
|
||||||
showHeader: true,
|
|
||||||
transparency: 0.8,
|
|
||||||
colorMode: "primary",
|
|
||||||
customColor: "#ffffff",
|
|
||||||
showCpu: true,
|
|
||||||
showCpuGraph: true,
|
|
||||||
showCpuTemp: true,
|
|
||||||
showGpuTemp: false,
|
|
||||||
gpuPciId: "",
|
|
||||||
showMemory: true,
|
|
||||||
showMemoryGraph: true,
|
|
||||||
showNetwork: true,
|
|
||||||
showNetworkGraph: true,
|
|
||||||
showDisk: true,
|
|
||||||
showTopProcesses: false,
|
|
||||||
topProcessCount: 3,
|
|
||||||
topProcessSortBy: "cpu",
|
|
||||||
layoutMode: "auto",
|
|
||||||
graphInterval: 60,
|
|
||||||
x: -1,
|
|
||||||
y: -1,
|
|
||||||
width: 320,
|
|
||||||
height: 480,
|
|
||||||
displayPreferences: ["all"]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDesktopWidgetInstance(widgetType, name, config) {
|
|
||||||
const id = "dw_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
|
||||||
const instance = {
|
|
||||||
id: id,
|
|
||||||
widgetType: widgetType,
|
|
||||||
name: name || widgetType,
|
|
||||||
enabled: true,
|
|
||||||
config: config || {},
|
|
||||||
positions: {}
|
|
||||||
};
|
|
||||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
||||||
instances.push(instance);
|
|
||||||
desktopWidgetInstances = instances;
|
|
||||||
saveSettings();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateDesktopWidgetInstance(instanceId, updates) {
|
|
||||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
||||||
const idx = instances.findIndex(inst => inst.id === instanceId);
|
|
||||||
if (idx === -1)
|
|
||||||
return;
|
|
||||||
Object.assign(instances[idx], updates);
|
|
||||||
desktopWidgetInstances = instances;
|
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateDesktopWidgetInstanceConfig(instanceId, configUpdates) {
|
|
||||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
||||||
const idx = instances.findIndex(inst => inst.id === instanceId);
|
|
||||||
if (idx === -1)
|
|
||||||
return;
|
|
||||||
instances[idx].config = Object.assign({}, instances[idx].config || {}, configUpdates);
|
|
||||||
desktopWidgetInstances = instances;
|
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateDesktopWidgetInstancePosition(instanceId, screenKey, positionUpdates) {
|
|
||||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
||||||
const idx = instances.findIndex(inst => inst.id === instanceId);
|
|
||||||
if (idx === -1)
|
|
||||||
return;
|
|
||||||
if (!instances[idx].positions)
|
|
||||||
instances[idx].positions = {};
|
|
||||||
instances[idx].positions[screenKey] = Object.assign({}, instances[idx].positions[screenKey] || {}, positionUpdates);
|
|
||||||
desktopWidgetInstances = instances;
|
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDesktopWidgetInstance(instanceId) {
|
|
||||||
const instances = (desktopWidgetInstances || []).filter(inst => inst.id !== instanceId);
|
|
||||||
desktopWidgetInstances = instances;
|
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
function duplicateDesktopWidgetInstance(instanceId) {
|
|
||||||
const source = getDesktopWidgetInstance(instanceId);
|
|
||||||
if (!source)
|
|
||||||
return null;
|
|
||||||
const newId = "dw_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
|
|
||||||
const instance = {
|
|
||||||
id: newId,
|
|
||||||
widgetType: source.widgetType,
|
|
||||||
name: source.name + " (Copy)",
|
|
||||||
enabled: source.enabled,
|
|
||||||
config: JSON.parse(JSON.stringify(source.config || {})),
|
|
||||||
positions: {}
|
|
||||||
};
|
|
||||||
const instances = JSON.parse(JSON.stringify(desktopWidgetInstances || []));
|
|
||||||
instances.push(instance);
|
|
||||||
desktopWidgetInstances = instances;
|
|
||||||
saveSettings();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDesktopWidgetInstance(instanceId) {
|
|
||||||
return (desktopWidgetInstances || []).find(inst => inst.id === instanceId) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDesktopWidgetInstancesOfType(widgetType) {
|
|
||||||
return (desktopWidgetInstances || []).filter(inst => inst.widgetType === widgetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEnabledDesktopWidgetInstances() {
|
|
||||||
return (desktopWidgetInstances || []).filter(inst => inst.enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
signal forceDankBarLayoutRefresh
|
signal forceDankBarLayoutRefresh
|
||||||
signal forceDockLayoutRefresh
|
signal forceDockLayoutRefresh
|
||||||
signal widgetDataChanged
|
signal widgetDataChanged
|
||||||
@@ -663,12 +421,10 @@ 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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -702,25 +458,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
|
||||||
fi
|
else
|
||||||
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]);
|
||||||
}
|
}
|
||||||
@@ -733,36 +489,36 @@ Singleton {
|
|||||||
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) {
|
||||||
@@ -787,6 +543,7 @@ Singleton {
|
|||||||
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);
|
||||||
@@ -833,42 +590,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function detectAvailableIconThemes() {
|
function detectAvailableIconThemes() {
|
||||||
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS") || "";
|
Processes.detectIcons();
|
||||||
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() {
|
||||||
@@ -932,14 +654,15 @@ Singleton {
|
|||||||
return barHeight + spacing + bottomGap - gothOffset + Theme.popupDistance;
|
return barHeight + spacing + bottomGap - gothOffset + Theme.popupDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPopupTriggerPosition(pos, screen, barThickness, widgetWidth, barSpacing, barPosition, barConfig) {
|
function getPopupTriggerPosition(globalPos, screen, barThickness, widgetWidth, barSpacing, barPosition, barConfig) {
|
||||||
const relativeX = pos.x;
|
const screenX = screen ? screen.x : 0;
|
||||||
const relativeY = pos.y;
|
const screenY = screen ? screen.y : 0;
|
||||||
|
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 rawBottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
|
const bottomGap = 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);
|
||||||
@@ -1000,7 +723,7 @@ Singleton {
|
|||||||
let leftBar = 0;
|
let leftBar = 0;
|
||||||
let rightBar = 0;
|
let rightBar = 0;
|
||||||
|
|
||||||
for (var i = 0; i < enabledBars.length; i++) {
|
for (let 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;
|
||||||
@@ -1070,7 +793,7 @@ Singleton {
|
|||||||
|
|
||||||
if (barConfig) {
|
if (barConfig) {
|
||||||
const enabledBars = getEnabledBarConfigs();
|
const enabledBars = getEnabledBarConfigs();
|
||||||
for (var i = 0; i < enabledBars.length; i++) {
|
for (let 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;
|
||||||
@@ -1190,7 +913,7 @@ Singleton {
|
|||||||
updateBarConfigs();
|
updateBarConfigs();
|
||||||
|
|
||||||
if (positionChanged) {
|
if (positionChanged) {
|
||||||
NotificationService.dismissAllPopups();
|
NotificationService.clearAllPopups();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1202,7 +925,7 @@ Singleton {
|
|||||||
const conflicts = [];
|
const conflicts = [];
|
||||||
const enabledBars = getEnabledBarConfigs();
|
const enabledBars = getEnabledBarConfigs();
|
||||||
|
|
||||||
for (var i = 0; i < enabledBars.length; i++) {
|
for (let i = 0; i < enabledBars.length; i++) {
|
||||||
const other = enabledBars[i];
|
const other = enabledBars[i];
|
||||||
if (other.id === barId)
|
if (other.id === barId)
|
||||||
continue;
|
continue;
|
||||||
@@ -1215,9 +938,9 @@ Singleton {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@@ -1225,9 +948,9 @@ Singleton {
|
|||||||
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"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1249,7 +972,7 @@ Singleton {
|
|||||||
|
|
||||||
function getScreensSortedByPosition() {
|
function getScreensSortedByPosition() {
|
||||||
const screens = [];
|
const screens = [];
|
||||||
for (var i = 0; i < Quickshell.screens.length; i++) {
|
for (let 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) => {
|
||||||
@@ -1266,7 +989,7 @@ Singleton {
|
|||||||
const sorted = getScreensSortedByPosition();
|
const sorted = getScreensSortedByPosition();
|
||||||
let modelCount = 0;
|
let modelCount = 0;
|
||||||
let screenIndex = -1;
|
let screenIndex = -1;
|
||||||
for (var i = 0; i < sorted.length; i++) {
|
for (let 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;
|
||||||
@@ -1335,7 +1058,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sendTestNotifications() {
|
function sendTestNotifications() {
|
||||||
NotificationService.dismissAllPopups();
|
NotificationService.clearAllPopups();
|
||||||
sendTestNotification(0);
|
sendTestNotification(0);
|
||||||
testNotifTimer1.start();
|
testNotifTimer1.start();
|
||||||
testNotifTimer2.start();
|
testNotifTimer2.start();
|
||||||
@@ -1464,7 +1187,7 @@ Singleton {
|
|||||||
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) {
|
||||||
@@ -1493,7 +1216,7 @@ Singleton {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateBarConfig(defaultBar.id, {
|
updateBarConfig(defaultBar.id, {
|
||||||
"position": position
|
position: position
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1501,7 +1224,7 @@ Singleton {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -1511,7 +1234,7 @@ Singleton {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -1521,7 +1244,7 @@ Singleton {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -1534,9 +1257,9 @@ Singleton {
|
|||||||
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);
|
||||||
@@ -1580,46 +1303,11 @@ Singleton {
|
|||||||
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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1657,87 +1345,6 @@ Singleton {
|
|||||||
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
|
||||||
}
|
}
|
||||||
@@ -1789,8 +1396,6 @@ Singleton {
|
|||||||
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,430 +1,545 @@
|
|||||||
// 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"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
purple: {
|
LIGHT: {
|
||||||
name: "Purple",
|
blue: {
|
||||||
primary: "#D0BCFF",
|
name: "Blue Light",
|
||||||
primaryText: "#381E72",
|
primary: "#1976d2",
|
||||||
primaryContainer: "#4F378B",
|
primaryText: "#ffffff",
|
||||||
secondary: "#CCC2DC",
|
primaryContainer: "#e3f2fd",
|
||||||
surface: "#141218",
|
secondary: "#42a5f5",
|
||||||
surfaceText: "#e6e0e9",
|
surface: "#f7f9ff",
|
||||||
surfaceVariant: "#49454e",
|
surfaceText: "#181c20",
|
||||||
surfaceVariantText: "#cac4cf",
|
surfaceVariant: "#dee3eb",
|
||||||
surfaceTint: "#D0BCFF",
|
surfaceVariantText: "#42474e",
|
||||||
background: "#141218",
|
surfaceTint: "#1976d2",
|
||||||
backgroundText: "#e6e0e9",
|
background: "#f7f9ff",
|
||||||
outline: "#948f99",
|
backgroundText: "#181c20",
|
||||||
surfaceContainer: "#211f24",
|
outline: "#72777f",
|
||||||
surfaceContainerHigh: "#2b292f",
|
surfaceContainer: "#eceef4",
|
||||||
surfaceContainerHighest: "#36343a",
|
surfaceContainerHigh: "#e6e8ee",
|
||||||
},
|
surfaceContainerHighest: "#e0e2e8"
|
||||||
green: {
|
},
|
||||||
name: "Green",
|
purple: {
|
||||||
primary: "#4caf50",
|
name: "Purple Light",
|
||||||
primaryText: "#000000",
|
primary: "#6750A4",
|
||||||
primaryContainer: "#1b5e20",
|
primaryText: "#ffffff",
|
||||||
secondary: "#81c995",
|
primaryContainer: "#EADDFF",
|
||||||
surface: "#10140f",
|
secondary: "#625B71",
|
||||||
surfaceText: "#e0e4db",
|
surface: "#fef7ff",
|
||||||
surfaceVariant: "#424940",
|
surfaceText: "#1d1b20",
|
||||||
surfaceVariantText: "#c2c9bd",
|
surfaceVariant: "#e7e0eb",
|
||||||
surfaceTint: "#81c995",
|
surfaceVariantText: "#49454e",
|
||||||
background: "#10140f",
|
surfaceTint: "#6750A4",
|
||||||
backgroundText: "#e0e4db",
|
background: "#fef7ff",
|
||||||
outline: "#8c9388",
|
backgroundText: "#1d1b20",
|
||||||
surfaceContainer: "#1d211b",
|
outline: "#7a757f",
|
||||||
surfaceContainerHigh: "#272b25",
|
surfaceContainer: "#f2ecf4",
|
||||||
surfaceContainerHighest: "#323630",
|
surfaceContainerHigh: "#ece6ee",
|
||||||
},
|
surfaceContainerHighest: "#e6e0e9"
|
||||||
orange: {
|
},
|
||||||
name: "Orange",
|
green: {
|
||||||
primary: "#ff6d00",
|
name: "Green Light",
|
||||||
primaryText: "#000000",
|
primary: "#2e7d32",
|
||||||
primaryContainer: "#3e2723",
|
primaryText: "#ffffff",
|
||||||
secondary: "#ffb74d",
|
primaryContainer: "#e8f5e8",
|
||||||
surface: "#1a120e",
|
secondary: "#4caf50",
|
||||||
surfaceText: "#f0dfd8",
|
surface: "#f7fbf1",
|
||||||
surfaceVariant: "#52443d",
|
surfaceText: "#191d17",
|
||||||
surfaceVariantText: "#d7c2b9",
|
surfaceVariant: "#dee5d8",
|
||||||
surfaceTint: "#ffb74d",
|
surfaceVariantText: "#424940",
|
||||||
background: "#1a120e",
|
surfaceTint: "#2e7d32",
|
||||||
backgroundText: "#f0dfd8",
|
background: "#f7fbf1",
|
||||||
outline: "#a08d85",
|
backgroundText: "#191d17",
|
||||||
surfaceContainer: "#271e1a",
|
outline: "#72796f",
|
||||||
surfaceContainerHigh: "#322824",
|
surfaceContainer: "#ecefe6",
|
||||||
surfaceContainerHighest: "#3d332e",
|
surfaceContainerHigh: "#e6e9e0",
|
||||||
},
|
surfaceContainerHighest: "#e0e4db"
|
||||||
red: {
|
},
|
||||||
name: "Red",
|
orange: {
|
||||||
primary: "#f44336",
|
name: "Orange Light",
|
||||||
primaryText: "#000000",
|
primary: "#e65100",
|
||||||
primaryContainer: "#4a0e0e",
|
primaryText: "#ffffff",
|
||||||
secondary: "#f28b82",
|
primaryContainer: "#ffecb3",
|
||||||
surface: "#1a1110",
|
secondary: "#ff9800",
|
||||||
surfaceText: "#f1dedc",
|
surface: "#fff8f6",
|
||||||
surfaceVariant: "#534341",
|
surfaceText: "#221a16",
|
||||||
surfaceVariantText: "#d8c2be",
|
surfaceVariant: "#f4ded5",
|
||||||
surfaceTint: "#f28b82",
|
surfaceVariantText: "#52443d",
|
||||||
background: "#1a1110",
|
surfaceTint: "#e65100",
|
||||||
backgroundText: "#f1dedc",
|
background: "#fff8f6",
|
||||||
outline: "#a08c89",
|
backgroundText: "#221a16",
|
||||||
surfaceContainer: "#271d1c",
|
outline: "#85736c",
|
||||||
surfaceContainerHigh: "#322826",
|
surfaceContainer: "#fceae3",
|
||||||
surfaceContainerHighest: "#3d3231",
|
surfaceContainerHigh: "#f6e5de",
|
||||||
},
|
surfaceContainerHighest: "#f0dfd8"
|
||||||
cyan: {
|
},
|
||||||
name: "Cyan",
|
red: {
|
||||||
primary: "#00bcd4",
|
name: "Red Light",
|
||||||
primaryText: "#000000",
|
primary: "#d32f2f",
|
||||||
primaryContainer: "#004d5c",
|
primaryText: "#ffffff",
|
||||||
secondary: "#4dd0e1",
|
primaryContainer: "#ffebee",
|
||||||
surface: "#0e1416",
|
secondary: "#f44336",
|
||||||
surfaceText: "#dee3e5",
|
surface: "#fff8f7",
|
||||||
surfaceVariant: "#3f484a",
|
surfaceText: "#231918",
|
||||||
surfaceVariantText: "#bfc8ca",
|
surfaceVariant: "#f5ddda",
|
||||||
surfaceTint: "#4dd0e1",
|
surfaceVariantText: "#534341",
|
||||||
background: "#0e1416",
|
surfaceTint: "#d32f2f",
|
||||||
backgroundText: "#dee3e5",
|
background: "#fff8f7",
|
||||||
outline: "#899295",
|
backgroundText: "#231918",
|
||||||
surfaceContainer: "#1b2122",
|
outline: "#857370",
|
||||||
surfaceContainerHigh: "#252b2c",
|
surfaceContainer: "#fceae7",
|
||||||
surfaceContainerHighest: "#303637",
|
surfaceContainerHigh: "#f7e4e1",
|
||||||
},
|
surfaceContainerHighest: "#f1dedc"
|
||||||
pink: {
|
},
|
||||||
name: "Pink",
|
cyan: {
|
||||||
primary: "#e91e63",
|
name: "Cyan Light",
|
||||||
primaryText: "#000000",
|
primary: "#0097a7",
|
||||||
primaryContainer: "#4a0e2f",
|
primaryText: "#ffffff",
|
||||||
secondary: "#f8bbd9",
|
primaryContainer: "#e0f2f1",
|
||||||
surface: "#191112",
|
secondary: "#00bcd4",
|
||||||
surfaceText: "#f0dee0",
|
surface: "#f5fafc",
|
||||||
surfaceVariant: "#524345",
|
surfaceText: "#171d1e",
|
||||||
surfaceVariantText: "#d6c2c3",
|
surfaceVariant: "#dbe4e6",
|
||||||
surfaceTint: "#f8bbd9",
|
surfaceVariantText: "#3f484a",
|
||||||
background: "#191112",
|
surfaceTint: "#0097a7",
|
||||||
backgroundText: "#f0dee0",
|
background: "#f5fafc",
|
||||||
outline: "#9f8c8e",
|
backgroundText: "#171d1e",
|
||||||
surfaceContainer: "#261d1e",
|
outline: "#6f797b",
|
||||||
surfaceContainerHigh: "#312829",
|
surfaceContainer: "#e9eff0",
|
||||||
surfaceContainerHighest: "#3c3233",
|
surfaceContainerHigh: "#e3e9eb",
|
||||||
},
|
surfaceContainerHighest: "#dee3e5"
|
||||||
amber: {
|
},
|
||||||
name: "Amber",
|
pink: {
|
||||||
primary: "#ffc107",
|
name: "Pink Light",
|
||||||
primaryText: "#000000",
|
primary: "#c2185b",
|
||||||
primaryContainer: "#4a3c00",
|
primaryText: "#ffffff",
|
||||||
secondary: "#ffd54f",
|
primaryContainer: "#fce4ec",
|
||||||
surface: "#17130b",
|
secondary: "#e91e63",
|
||||||
surfaceText: "#ebe1d4",
|
surface: "#fff8f7",
|
||||||
surfaceVariant: "#4d4639",
|
surfaceText: "#22191a",
|
||||||
surfaceVariantText: "#d0c5b4",
|
surfaceVariant: "#f3dddf",
|
||||||
surfaceTint: "#ffd54f",
|
surfaceVariantText: "#524345",
|
||||||
background: "#17130b",
|
surfaceTint: "#c2185b",
|
||||||
backgroundText: "#ebe1d4",
|
background: "#fff8f7",
|
||||||
outline: "#998f80",
|
backgroundText: "#22191a",
|
||||||
surfaceContainer: "#231f17",
|
outline: "#847375",
|
||||||
surfaceContainerHigh: "#2e2921",
|
surfaceContainer: "#fbeaeb",
|
||||||
surfaceContainerHighest: "#39342b",
|
surfaceContainerHigh: "#f5e4e5",
|
||||||
},
|
surfaceContainerHighest: "#f0dee0"
|
||||||
coral: {
|
},
|
||||||
name: "Coral",
|
amber: {
|
||||||
primary: "#ffb4ab",
|
name: "Amber Light",
|
||||||
primaryText: "#000000",
|
primary: "#ff8f00",
|
||||||
primaryContainer: "#8c1d18",
|
primaryText: "#000000",
|
||||||
secondary: "#f9dedc",
|
primaryContainer: "#fff8e1",
|
||||||
surface: "#1a1110",
|
secondary: "#ffc107",
|
||||||
surfaceText: "#f1dedc",
|
surface: "#fff8f2",
|
||||||
surfaceVariant: "#534341",
|
surfaceText: "#1f1b13",
|
||||||
surfaceVariantText: "#d8c2bf",
|
surfaceVariant: "#ede1cf",
|
||||||
surfaceTint: "#ffb4ab",
|
surfaceVariantText: "#4d4639",
|
||||||
background: "#1a1110",
|
surfaceTint: "#ff8f00",
|
||||||
backgroundText: "#f1dedc",
|
background: "#fff8f2",
|
||||||
outline: "#a08c8a",
|
backgroundText: "#1f1b13",
|
||||||
surfaceContainer: "#271d1c",
|
outline: "#7f7667",
|
||||||
surfaceContainerHigh: "#322826",
|
surfaceContainer: "#f6ecdf",
|
||||||
surfaceContainerHighest: "#3d3231",
|
surfaceContainerHigh: "#f1e7d9",
|
||||||
},
|
surfaceContainerHighest: "#ebe1d4"
|
||||||
monochrome: {
|
},
|
||||||
name: "Monochrome",
|
coral: {
|
||||||
primary: "#ffffff",
|
name: "Coral Light",
|
||||||
primaryText: "#2b303c",
|
primary: "#8c1d18",
|
||||||
primaryContainer: "#424753",
|
primaryText: "#ffffff",
|
||||||
secondary: "#c4c6d0",
|
primaryContainer: "#ffdad6",
|
||||||
surface: "#2a2a2a",
|
secondary: "#ff5449",
|
||||||
surfaceText: "#e4e2e3",
|
surface: "#fff8f7",
|
||||||
surfaceVariant: "#474648",
|
surfaceText: "#231918",
|
||||||
surfaceVariantText: "#c8c6c7",
|
surfaceVariant: "#f5ddda",
|
||||||
surfaceTint: "#c2c6d6",
|
surfaceVariantText: "#534341",
|
||||||
background: "#131315",
|
surfaceTint: "#8c1d18",
|
||||||
backgroundText: "#e4e2e3",
|
background: "#fff8f7",
|
||||||
outline: "#929092",
|
backgroundText: "#231918",
|
||||||
surfaceContainer: "#353535",
|
outline: "#857371",
|
||||||
surfaceContainerHigh: "#424242",
|
surfaceContainer: "#fceae7",
|
||||||
surfaceContainerHighest: "#505050",
|
surfaceContainerHigh: "#f6e4e2",
|
||||||
error: "#ffb4ab",
|
surfaceContainerHighest: "#f1dedc"
|
||||||
warning: "#3f4759",
|
},
|
||||||
info: "#595e6c",
|
monochrome: {
|
||||||
matugen_type: "scheme-monochrome",
|
name: "Monochrome Light",
|
||||||
},
|
primary: "#2b303c",
|
||||||
},
|
primaryText: "#ffffff",
|
||||||
LIGHT: {
|
primaryContainer: "#d6d7dc",
|
||||||
blue: {
|
secondary: "#4a4d56",
|
||||||
name: "Blue Light",
|
surface: "#f5f5f6",
|
||||||
primary: "#1976d2",
|
surfaceText: "#2a2a2a",
|
||||||
primaryText: "#ffffff",
|
surfaceVariant: "#e0e0e2",
|
||||||
primaryContainer: "#e3f2fd",
|
surfaceVariantText: "#424242",
|
||||||
secondary: "#42a5f5",
|
surfaceTint: "#5a5f6e",
|
||||||
surface: "#f7f9ff",
|
background: "#ffffff",
|
||||||
surfaceText: "#181c20",
|
backgroundText: "#1a1a1a",
|
||||||
surfaceVariant: "#dee3eb",
|
outline: "#757577",
|
||||||
surfaceVariantText: "#42474e",
|
surfaceContainer: "#e8e8ea",
|
||||||
surfaceTint: "#1976d2",
|
surfaceContainerHigh: "#dcdcde",
|
||||||
background: "#f7f9ff",
|
surfaceContainerHighest: "#d0d0d2",
|
||||||
backgroundText: "#181c20",
|
error: "#ba1a1a",
|
||||||
outline: "#72777f",
|
warning: "#f9e79f",
|
||||||
surfaceContainer: "#eceef4",
|
info: "#5d6475",
|
||||||
surfaceContainerHigh: "#e6e8ee",
|
matugen_type: "scheme-monochrome"
|
||||||
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: [
|
variants: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"]
|
||||||
"blue",
|
},
|
||||||
"purple",
|
CATPPUCCIN: {
|
||||||
"green",
|
name: "Catppuccin",
|
||||||
"orange",
|
variants: Object.keys(CatppuccinVariants)
|
||||||
"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) {
|
||||||
const themes = getAvailableThemes(isLight);
|
if (isCatppuccinVariant(themeName)) {
|
||||||
return themes[themeName] || themes.blue;
|
return getCatppuccinTheme(themeName, isLight)
|
||||||
|
}
|
||||||
|
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,9 +92,6 @@ 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]);
|
||||||
@@ -260,7 +257,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 || surfaceContainerHigh
|
property color surfaceContainerHighest: currentThemeData.surfaceContainerHighest
|
||||||
|
|
||||||
property color onSurface: surfaceText
|
property color onSurface: surfaceText
|
||||||
property color onSurfaceVariant: surfaceVariantText
|
property color onSurfaceVariant: surfaceVariantText
|
||||||
@@ -477,28 +474,24 @@ Singleton {
|
|||||||
|
|
||||||
if (themeName === dynamic) {
|
if (themeName === dynamic) {
|
||||||
currentTheme = dynamic;
|
currentTheme = dynamic;
|
||||||
if (currentThemeCategory !== "registry")
|
currentThemeCategory = dynamic;
|
||||||
currentThemeCategory = dynamic;
|
|
||||||
} else if (themeName === custom) {
|
} else if (themeName === custom) {
|
||||||
currentTheme = custom;
|
currentTheme = custom;
|
||||||
if (currentThemeCategory !== "registry")
|
currentThemeCategory = custom;
|
||||||
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 (currentThemeCategory !== "registry") {
|
if (StockThemes.isCatppuccinVariant(themeName)) {
|
||||||
|
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();
|
||||||
@@ -560,103 +553,62 @@ 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) {
|
||||||
baseColors = themeData[colorMode] || themeData.dark || themeData.light || {};
|
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark";
|
||||||
|
const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light;
|
||||||
|
customThemeData = selectedTheme;
|
||||||
} else {
|
} else {
|
||||||
baseColors = themeData;
|
customThemeData = 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
|
||||||
@@ -687,9 +639,10 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property color widgetBaseHoverColor: {
|
property var widgetBaseHoverColor: {
|
||||||
const blended = blend(widgetBaseBackgroundColor, primary, 0.1);
|
const baseColor = widgetBaseBackgroundColor;
|
||||||
return withAlpha(blended, Math.max(0.3, blended.a));
|
const factor = 1.2;
|
||||||
|
return isLightMode ? Qt.darker(baseColor, factor) : Qt.lighter(baseColor, factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
property color widgetIconColor: {
|
property color widgetIconColor: {
|
||||||
@@ -876,7 +829,7 @@ Singleton {
|
|||||||
if (typeof SettingsData !== "undefined") {
|
if (typeof SettingsData !== "undefined") {
|
||||||
const skipTemplates = [];
|
const skipTemplates = [];
|
||||||
if (!SettingsData.runDmsMatugenTemplates) {
|
if (!SettingsData.runDmsMatugenTemplates) {
|
||||||
skipTemplates.push("gtk", "neovim", "niri", "qt5ct", "qt6ct", "firefox", "pywalfox", "vesktop", "equibop", "ghostty", "kitty", "foot", "alacritty", "wezterm", "dgop", "kcolorscheme", "vscode");
|
skipTemplates.push("gtk", "niri", "qt5ct", "qt6ct", "firefox", "pywalfox", "vesktop", "ghostty", "kitty", "foot", "alacritty", "wezterm", "dgop", "kcolorscheme", "vscode");
|
||||||
} else {
|
} else {
|
||||||
if (!SettingsData.matugenTemplateGtk)
|
if (!SettingsData.matugenTemplateGtk)
|
||||||
skipTemplates.push("gtk");
|
skipTemplates.push("gtk");
|
||||||
@@ -892,16 +845,12 @@ 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)
|
||||||
@@ -950,48 +899,8 @@ Singleton {
|
|||||||
|
|
||||||
let darkTheme, lightTheme;
|
let darkTheme, lightTheme;
|
||||||
if (currentTheme === "custom") {
|
if (currentTheme === "custom") {
|
||||||
if (customThemeRawData && (customThemeRawData.dark || customThemeRawData.light)) {
|
darkTheme = customThemeData;
|
||||||
darkTheme = customThemeRawData.dark || customThemeRawData.light;
|
lightTheme = customThemeData;
|
||||||
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);
|
||||||
@@ -1009,7 +918,6 @@ 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)
|
||||||
@@ -1022,7 +930,7 @@ Singleton {
|
|||||||
"color": String(lightVal || darkVal)
|
"color": String(lightVal || darkVal)
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"color": String((isLight && lightVal) ? lightVal : darkVal)
|
"color": String(darkVal || lightVal)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,9 @@ 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
|
||||||
};
|
};
|
||||||
@@ -68,18 +65,12 @@ 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,4 +1,5 @@
|
|||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -10,20 +11,61 @@ 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 {
|
||||||
@@ -32,8 +74,7 @@ Singleton {
|
|||||||
|
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
if (!settingsRoot)
|
if (!settingsRoot) return;
|
||||||
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++) {
|
||||||
@@ -54,9 +95,8 @@ 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)
|
if (!settingsRoot) return;
|
||||||
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) {
|
||||||
@@ -73,9 +113,8 @@ 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)
|
if (!settingsRoot) return;
|
||||||
return;
|
|
||||||
settingsRoot.fprintdAvailable = (exitCode === 0);
|
settingsRoot.fprintdAvailable = (exitCode === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,9 +123,8 @@ Singleton {
|
|||||||
command: ["test", "-f", settingsRoot?.pluginSettingsPath || ""]
|
command: ["test", "-f", settingsRoot?.pluginSettingsPath || ""]
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
onExited: function (exitCode) {
|
onExited: function(exitCode) {
|
||||||
if (!settingsRoot)
|
if (!settingsRoot) return;
|
||||||
return;
|
|
||||||
settingsRoot.pluginSettingsFileExists = (exitCode === 0);
|
settingsRoot.pluginSettingsFileExists = (exitCode === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ 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" },
|
||||||
@@ -20,8 +18,6 @@ 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 },
|
||||||
@@ -55,12 +51,9 @@ 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 },
|
||||||
|
|
||||||
@@ -87,13 +80,11 @@ var SPEC = {
|
|||||||
maxWorkspaceIcons: { def: 3 },
|
maxWorkspaceIcons: { def: 3 },
|
||||||
workspacesPerMonitor: { def: true },
|
workspacesPerMonitor: { def: true },
|
||||||
showOccupiedWorkspacesOnly: { def: false },
|
showOccupiedWorkspacesOnly: { def: false },
|
||||||
reverseScrolling: { def: false },
|
|
||||||
dwlShowAllTags: { def: false },
|
dwlShowAllTags: { def: false },
|
||||||
workspaceNameIcons: { def: {} },
|
workspaceNameIcons: { def: {} },
|
||||||
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 },
|
||||||
@@ -163,7 +154,6 @@ 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 },
|
||||||
@@ -188,12 +178,10 @@ 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 },
|
||||||
@@ -213,7 +201,6 @@ 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 },
|
||||||
@@ -271,8 +258,6 @@ 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",
|
||||||
@@ -305,7 +290,6 @@ 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,
|
||||||
@@ -314,52 +298,7 @@ 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,101 +119,6 @@ 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,8 +58,6 @@ Item {
|
|||||||
|
|
||||||
WallpaperBackground {}
|
WallpaperBackground {}
|
||||||
|
|
||||||
DesktopWidgetLayer {}
|
|
||||||
|
|
||||||
Lock {
|
Lock {
|
||||||
id: lock
|
id: lock
|
||||||
}
|
}
|
||||||
@@ -510,22 +508,6 @@ 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,58 +893,4 @@ 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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user