1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00

Compare commits

...

12 Commits

Author SHA1 Message Date
bbedward
7fb358bada v1.0.2 2025-12-12 10:18:54 -05:00
bbedward
73cf3130e1 ci: disable pkg builds from main release wf 2025-12-12 10:18:31 -05:00
bbedward
119b5df6df gamma: fix initial night mode enablement 2025-12-12 10:15:38 -05:00
bbedward
8ede810d32 settings: make default height screen-aware 2025-12-12 10:14:33 -05:00
bbedward
830dd93af5 chore: bump version to v1.0.1 2025-12-12 10:04:47 -05:00
bbedward
75f28c5ea7 ci: switch to dispatch-based release flow 2025-12-12 10:04:20 -05:00
bbedward
6c9b8c590e dankinstall: call add-wants for niri/hyprland with dms service 2025-12-12 10:04:20 -05:00
bbedward
24d9b77307 niri: fix keybind handling of cooldown-ms parameter 2025-12-12 10:04:20 -05:00
bbedward
d4be68912c workspaces: make icons scale with bar size, fixi valign of numbers fixes #990 2025-12-12 10:04:20 -05:00
bbedward
a443721000 core: fix socket reported CLI version 2025-12-12 10:03:32 -05:00
bbedward
786b097187 plugins: hide uninstall and update buttons for system plugins 2025-12-12 10:03:18 -05:00
bbedward
8ca60c7d2a dwl: fix layout popout not opening fixes #980 2025-12-12 10:03:04 -05:00
20 changed files with 466 additions and 301 deletions

View File

@@ -1,16 +1,19 @@
name: Release name: Release
on: on:
push: workflow_dispatch:
tags: inputs:
- "v*" tag:
description: "Tag to release (e.g., v1.0.1)"
required: true
type: string
permissions: permissions:
contents: write contents: write
actions: write actions: write
concurrency: concurrency:
group: release-${{ github.ref_name }} group: release-${{ inputs.tag }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
@@ -24,10 +27,14 @@ jobs:
run: run:
working-directory: core working-directory: core
env:
TAG: ${{ inputs.tag }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: ${{ inputs.tag }}
fetch-depth: 0 fetch-depth: 0
- name: Set up Go - name: Set up Go
@@ -54,7 +61,7 @@ jobs:
run: | run: |
set -eux set -eux
cd cmd/dankinstall cd cmd/dankinstall
go build -trimpath -ldflags "-s -w -X main.Version=${GITHUB_REF#refs/tags/}" \ go build -trimpath -ldflags "-s -w -X main.Version=${TAG}" \
-o ../../dankinstall-${{ matrix.arch }} -o ../../dankinstall-${{ matrix.arch }}
cd ../.. cd ../..
gzip -9 -k dankinstall-${{ matrix.arch }} gzip -9 -k dankinstall-${{ matrix.arch }}
@@ -68,7 +75,7 @@ jobs:
run: | run: |
set -eux set -eux
cd cmd/dms cd cmd/dms
go build -trimpath -ldflags "-s -w -X main.Version=${GITHUB_REF#refs/tags/}" \ go build -trimpath -ldflags "-s -w -X main.Version=${TAG}" \
-o ../../dms-${{ matrix.arch }} -o ../../dms-${{ matrix.arch }}
cd ../.. cd ../..
gzip -9 -k dms-${{ matrix.arch }} gzip -9 -k dms-${{ matrix.arch }}
@@ -91,7 +98,7 @@ jobs:
run: | run: |
set -eux set -eux
cd cmd/dms cd cmd/dms
go build -trimpath -tags distro_binary -ldflags "-s -w -X main.Version=${GITHUB_REF#refs/tags/}" \ go build -trimpath -tags distro_binary -ldflags "-s -w -X main.Version=${TAG}" \
-o ../../dms-distropkg-${{ matrix.arch }} -o ../../dms-distropkg-${{ matrix.arch }}
cd ../.. cd ../..
gzip -9 -k dms-distropkg-${{ matrix.arch }} gzip -9 -k dms-distropkg-${{ matrix.arch }}
@@ -171,17 +178,18 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: [build-core] #, update-versions] needs: [build-core] #, update-versions]
env: env:
TAG: ${{ github.ref_name }} TAG: ${{ inputs.tag }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: ${{ inputs.tag }}
fetch-depth: 0 fetch-depth: 0
- name: Fetch updated tag after version bump - name: Fetch updated tag after version bump
run: | run: |
git fetch origin --force tag ${{ github.ref_name }} git fetch origin --force tag ${TAG}
git checkout ${{ github.ref_name }} git checkout ${TAG}
- name: Download core artifacts - name: Download core artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
@@ -388,288 +396,296 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
trigger-obs-update: # trigger-obs-update:
runs-on: ubuntu-latest # runs-on: ubuntu-latest
needs: release # needs: release
steps: # env:
- name: Checkout # TAG: ${{ inputs.tag }}
uses: actions/checkout@v4 # steps:
# - name: Checkout
# uses: actions/checkout@v4
# with:
# ref: ${{ inputs.tag }}
- name: Install OSC # - name: Install OSC
run: | # run: |
sudo apt-get update # sudo apt-get update
sudo apt-get install -y osc # sudo apt-get install -y osc
mkdir -p ~/.config/osc # mkdir -p ~/.config/osc
cat > ~/.config/osc/oscrc << EOF # cat > ~/.config/osc/oscrc << EOF
[general] # [general]
apiurl = https://api.opensuse.org # apiurl = https://api.opensuse.org
[https://api.opensuse.org] # [https://api.opensuse.org]
user = ${{ secrets.OBS_USERNAME }} # user = ${{ secrets.OBS_USERNAME }}
pass = ${{ secrets.OBS_PASSWORD }} # pass = ${{ secrets.OBS_PASSWORD }}
EOF # EOF
chmod 600 ~/.config/osc/oscrc # chmod 600 ~/.config/osc/oscrc
- name: Update OBS packages # - name: Update OBS packages
run: | # run: |
VERSION="${{ github.ref_name }}" # cd distro
cd distro # bash scripts/obs-upload.sh dms "Update to ${TAG}"
bash scripts/obs-upload.sh dms "Update to $VERSION"
trigger-ppa-update: # trigger-ppa-update:
runs-on: ubuntu-latest # runs-on: ubuntu-latest
needs: release # needs: release
steps: # env:
- name: Checkout # TAG: ${{ inputs.tag }}
uses: actions/checkout@v4 # steps:
# - name: Checkout
# uses: actions/checkout@v4
# with:
# ref: ${{ inputs.tag }}
- name: Install build dependencies # - name: Install build dependencies
run: | # run: |
sudo apt-get update # sudo apt-get update
sudo apt-get install -y \ # sudo apt-get install -y \
debhelper \ # debhelper \
devscripts \ # devscripts \
dput \ # dput \
lftp \ # lftp \
build-essential \ # build-essential \
fakeroot \ # fakeroot \
dpkg-dev # dpkg-dev
- name: Configure GPG # - name: Configure GPG
env: # env:
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }} # GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
run: | # run: |
echo "$GPG_KEY" | gpg --import # echo "$GPG_KEY" | gpg --import
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | awk '{print $2}' | cut -d'/' -f2) # 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 # echo "DEBSIGN_KEYID=$GPG_KEY_ID" >> $GITHUB_ENV
- name: Upload to PPA # - name: Upload to PPA
run: | # run: |
VERSION="${{ github.ref_name }}" # cd distro/ubuntu/ppa
cd distro/ubuntu/ppa # bash create-and-upload.sh ../dms dms questing
bash create-and-upload.sh ../dms dms questing
copr-build: # copr-build:
runs-on: ubuntu-latest # runs-on: ubuntu-latest
needs: release # needs: release
env: # env:
TAG: ${{ github.ref_name }} # TAG: ${{ inputs.tag }}
steps: # steps:
- name: Checkout repository # - name: Checkout repository
uses: actions/checkout@v4 # uses: actions/checkout@v4
# with:
# ref: ${{ inputs.tag }}
- name: Determine version # - name: Determine version
id: version # id: version
run: | # run: |
VERSION="${TAG#v}" # VERSION="${TAG#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT # echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Building DMS stable version: $VERSION" # echo "Building DMS stable version: $VERSION"
- name: Setup build environment # - name: Setup build environment
run: | # run: |
sudo apt-get update # sudo apt-get update
sudo apt-get install -y rpm wget curl jq gzip # sudo apt-get install -y rpm wget curl jq gzip
mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} # mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
- name: Download release assets # - name: Download release assets
run: | # run: |
VERSION="${{ steps.version.outputs.version }}" # VERSION="${{ steps.version.outputs.version }}"
cd ~/rpmbuild/SOURCES # cd ~/rpmbuild/SOURCES
wget "https://github.com/AvengeMedia/DankMaterialShell/releases/download/v${VERSION}/dms-qml.tar.gz" || { # 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}" # echo "Failed to download dms-qml.tar.gz for v${VERSION}"
exit 1 # exit 1
} # }
- name: Generate stable spec file # - name: Generate stable spec file
run: | # run: |
VERSION="${{ steps.version.outputs.version }}" # VERSION="${{ steps.version.outputs.version }}"
CHANGELOG_DATE="$(date '+%a %b %d %Y')" # CHANGELOG_DATE="$(date '+%a %b %d %Y')"
cat > ~/rpmbuild/SPECS/dms.spec <<'SPECEOF' # cat > ~/rpmbuild/SPECS/dms.spec <<'SPECEOF'
# Spec for DMS stable releases - Generated by GitHub Actions # # Spec for DMS stable releases - Generated by GitHub Actions
%global debug_package %{nil} # %global debug_package %{nil}
%global version VERSION_PLACEHOLDER # %global version VERSION_PLACEHOLDER
%global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors # %global pkg_summary DankMaterialShell - Material 3 inspired shell for Wayland compositors
Name: dms # Name: dms
Version: %{version} # Version: %{version}
Release: 1%{?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
Source0: dms-qml.tar.gz # Source0: dms-qml.tar.gz
BuildRequires: gzip # BuildRequires: gzip
BuildRequires: wget # BuildRequires: wget
BuildRequires: systemd-rpm-macros # BuildRequires: systemd-rpm-macros
Requires: (quickshell or quickshell-git) # Requires: (quickshell or quickshell-git)
Requires: accountsservice # Requires: accountsservice
Requires: dms-cli = %{version}-%{release} # Requires: dms-cli = %{version}-%{release}
Requires: dgop # Requires: dgop
Recommends: cava # Recommends: cava
Recommends: cliphist # Recommends: cliphist
Recommends: danksearch # Recommends: danksearch
Recommends: matugen # Recommends: matugen
Recommends: wl-clipboard # Recommends: wl-clipboard
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 and hyprland compositors. Features notifications,
app launcher, wallpaper customization, and fully customizable with plugins. # app launcher, wallpaper customization, and fully customizable with plugins.
Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets, # Includes auto-theming for GTK/Qt apps with matugen, 20+ customizable widgets,
process monitoring, notification center, clipboard history, dock, control center, # process monitoring, notification center, clipboard history, dock, control center,
lock screen, and comprehensive plugin system. # lock screen, and comprehensive plugin system.
%package -n dms-cli # %package -n dms-cli
Summary: DankMaterialShell CLI tool # Summary: DankMaterialShell CLI tool
License: MIT # License: MIT
URL: https://github.com/AvengeMedia/DankMaterialShell # URL: https://github.com/AvengeMedia/DankMaterialShell
%description -n dms-cli # %description -n dms-cli
Command-line interface for DankMaterialShell configuration and management. # 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 # %setup -q -c -n dms-qml
# Download architecture-specific binaries during build # # Download architecture-specific binaries during build
case "%{_arch}" in # case "%{_arch}" in
x86_64) # x86_64)
ARCH_SUFFIX="amd64" # ARCH_SUFFIX="amd64"
;; # ;;
aarch64) # aarch64)
ARCH_SUFFIX="arm64" # ARCH_SUFFIX="arm64"
;; # ;;
*) # *)
echo "Unsupported architecture: %{_arch}" # echo "Unsupported architecture: %{_arch}"
exit 1 # exit 1
;; # ;;
esac # esac
wget -O %{_builddir}/dms-cli.gz "https://github.com/AvengeMedia/DankMaterialShell/releases/latest/download/dms-distropkg-${ARCH_SUFFIX}.gz" || { # 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}" # echo "Failed to download dms-cli for architecture %{_arch}"
exit 1 # exit 1
} # }
gunzip -c %{_builddir}/dms-cli.gz > %{_builddir}/dms-cli # gunzip -c %{_builddir}/dms-cli.gz > %{_builddir}/dms-cli
chmod +x %{_builddir}/dms-cli # chmod +x %{_builddir}/dms-cli
%build # %build
%install # %install
install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms # install -Dm755 %{_builddir}/dms-cli %{buildroot}%{_bindir}/dms
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 || : # %{_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 zsh > %{buildroot}%{_datadir}/zsh/site-functions/_dms || :
%{_builddir}/dms-cli completion fish > %{buildroot}%{_datadir}/fish/vendor_completions.d/dms.fish || : # %{_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/systemd/dms.service %{buildroot}%{_userunitdir}/dms.service
install -Dm644 assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop # install -Dm644 assets/dms-open.desktop %{buildroot}%{_datadir}/applications/dms-open.desktop
install -Dm644 assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg # install -Dm644 assets/danklogo.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
install -dm755 %{buildroot}%{_datadir}/quickshell/dms # install -dm755 %{buildroot}%{_datadir}/quickshell/dms
cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/ # cp -r %{_builddir}/dms-qml/* %{buildroot}%{_datadir}/quickshell/dms/
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
rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro # rm -rf %{buildroot}%{_datadir}/quickshell/dms/distro
echo "%{version}" > %{buildroot}%{_datadir}/quickshell/dms/VERSION # echo "%{version}" > %{buildroot}%{_datadir}/quickshell/dms/VERSION
%posttrans # %posttrans
if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then # if [ -d "%{_sysconfdir}/xdg/quickshell/dms" ]; then
rmdir "%{_sysconfdir}/xdg/quickshell/dms" 2>/dev/null || true # rmdir "%{_sysconfdir}/xdg/quickshell/dms" 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
fi # fi
# Signal running DMS instances to reload # # Signal running DMS instances to reload
pkill -USR1 -x dms >/dev/null 2>&1 || : # pkill -USR1 -x dms >/dev/null 2>&1 || :
%files # %files
%license LICENSE # %license LICENSE
%doc README.md CONTRIBUTING.md # %doc README.md CONTRIBUTING.md
%{_datadir}/quickshell/dms/ # %{_datadir}/quickshell/dms/
%{_userunitdir}/dms.service # %{_userunitdir}/dms.service
%{_datadir}/applications/dms-open.desktop # %{_datadir}/applications/dms-open.desktop
%{_datadir}/icons/hicolor/scalable/apps/danklogo.svg # %{_datadir}/icons/hicolor/scalable/apps/danklogo.svg
%files -n dms-cli # %files -n dms-cli
%{_bindir}/dms # %{_bindir}/dms
%{_datadir}/bash-completion/completions/dms # %{_datadir}/bash-completion/completions/dms
%{_datadir}/zsh/site-functions/_dms # %{_datadir}/zsh/site-functions/_dms
%{_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-1 # * CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-1
- Stable release VERSION_PLACEHOLDER # - Stable release VERSION_PLACEHOLDER
- Built from GitHub release # - Built from GitHub release
SPECEOF # SPECEOF
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/dms.spec # sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/dms.spec
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/dms.spec # sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/dms.spec
- name: Build SRPM # - name: Build SRPM
id: build # id: build
run: | # run: |
cd ~/rpmbuild/SPECS # cd ~/rpmbuild/SPECS
rpmbuild -bs dms.spec # rpmbuild -bs dms.spec
SRPM=$(ls ~/rpmbuild/SRPMS/*.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
echo "srpm_name=$SRPM_NAME" >> $GITHUB_OUTPUT # echo "srpm_name=$SRPM_NAME" >> $GITHUB_OUTPUT
echo "SRPM built: $SRPM_NAME" # echo "SRPM built: $SRPM_NAME"
- name: Upload SRPM artifact # - name: Upload SRPM artifact
uses: actions/upload-artifact@v4 # uses: actions/upload-artifact@v4
with: # with:
name: dms-stable-srpm-${{ steps.version.outputs.version }} # name: 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
- name: Install Copr CLI # - name: Install Copr CLI
run: | # run: |
sudo apt-get install -y python3-pip # sudo apt-get install -y python3-pip
pip3 install copr-cli # pip3 install copr-cli
mkdir -p ~/.config # mkdir -p ~/.config
cat > ~/.config/copr << EOF # cat > ~/.config/copr << EOF
[copr-cli] # [copr-cli]
login = ${{ secrets.COPR_LOGIN }} # login = ${{ secrets.COPR_LOGIN }}
username = avengemedia # username = avengemedia
token = ${{ secrets.COPR_TOKEN }} # token = ${{ secrets.COPR_TOKEN }}
copr_url = https://copr.fedorainfracloud.org # copr_url = https://copr.fedorainfracloud.org
EOF # EOF
chmod 600 ~/.config/copr # chmod 600 ~/.config/copr
- 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 }}"
echo "Uploading SRPM to avengemedia/dms..." # echo "Uploading SRPM to avengemedia/dms..."
BUILD_OUTPUT=$(copr-cli build avengemedia/dms "$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: https://copr.fedorainfracloud.org/coprs/avengemedia/dms/build/$BUILD_ID/" # echo "Build submitted: https://copr.fedorainfracloud.org/coprs/avengemedia/dms/build/$BUILD_ID/"
fi # fi

View File

@@ -155,6 +155,7 @@ func runShellInteractive(session bool) {
errChan <- fmt.Errorf("server panic: %v", r) errChan <- fmt.Errorf("server panic: %v", r)
} }
}() }()
server.CLIVersion = Version
if err := server.Start(false); err != nil { if err := server.Start(false); err != nil {
errChan <- fmt.Errorf("server error: %w", err) errChan <- fmt.Errorf("server error: %w", err)
} }

View File

@@ -344,7 +344,7 @@ func (a *ArchDistribution) InstallPackages(ctx context.Context, dependencies []d
a.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) a.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err))
} }
if err := a.EnableDMSService(ctx); err != nil { if err := a.EnableDMSService(ctx, wm); err != nil {
a.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) a.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
} }

View File

@@ -597,12 +597,24 @@ TERMINAL=%s
return nil return nil
} }
func (b *BaseDistribution) EnableDMSService(ctx context.Context) error { func (b *BaseDistribution) EnableDMSService(ctx context.Context, wm deps.WindowManager) error {
cmd := exec.CommandContext(ctx, "systemctl", "--user", "enable", "--now", "dms") cmd := exec.CommandContext(ctx, "systemctl", "--user", "enable", "--now", "dms")
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to enable dms service: %w", err) return fmt.Errorf("failed to enable dms service: %w", err)
} }
b.log("Enabled dms systemd user service") b.log("Enabled dms systemd user service")
switch wm {
case deps.WindowManagerNiri:
if err := exec.CommandContext(ctx, "systemctl", "--user", "add-wants", "niri.service", "dms").Run(); err != nil {
b.log("Warning: failed to add dms as a want for niri.service")
}
case deps.WindowManagerHyprland:
if err := exec.CommandContext(ctx, "systemctl", "--user", "add-wants", "hyprland-session.target", "dms").Run(); err != nil {
b.log("Warning: failed to add dms as a want for hyprland-session.target")
}
}
return nil return nil
} }

View File

@@ -312,7 +312,7 @@ func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies [
d.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) d.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err))
} }
if err := d.EnableDMSService(ctx); err != nil { if err := d.EnableDMSService(ctx, wm); err != nil {
d.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) d.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
} }

View File

@@ -353,7 +353,7 @@ func (f *FedoraDistribution) InstallPackages(ctx context.Context, dependencies [
f.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) f.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err))
} }
if err := f.EnableDMSService(ctx); err != nil { if err := f.EnableDMSService(ctx, wm); err != nil {
f.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) f.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
} }

View File

@@ -410,7 +410,7 @@ func (g *GentooDistribution) InstallPackages(ctx context.Context, dependencies [
g.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) g.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err))
} }
if err := g.EnableDMSService(ctx); err != nil { if err := g.EnableDMSService(ctx, wm); err != nil {
g.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) g.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
} }

View File

@@ -371,7 +371,7 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
o.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) o.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err))
} }
if err := o.EnableDMSService(ctx); err != nil { if err := o.EnableDMSService(ctx, wm); err != nil {
o.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) o.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
} }

View File

@@ -331,7 +331,7 @@ func (u *UbuntuDistribution) InstallPackages(ctx context.Context, dependencies [
u.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) u.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err))
} }
if err := u.EnableDMSService(ctx); err != nil { if err := u.EnableDMSService(ctx, wm); err != nil {
u.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) u.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
} }

View File

@@ -6,6 +6,7 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"sort" "sort"
"strconv"
"strings" "strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds" "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
@@ -153,6 +154,7 @@ func (n *NiriProvider) convertKeybind(kb *NiriKeyBinding, subcategory string, co
Subcategory: subcategory, Subcategory: subcategory,
Source: source, Source: source,
HideOnOverlay: kb.HideOnOverlay, HideOnOverlay: kb.HideOnOverlay,
CooldownMs: kb.CooldownMs,
} }
if source == "dms" && conflicts != nil { if source == "dms" && conflicts != nil {
@@ -310,7 +312,9 @@ func (n *NiriProvider) extractOptions(node *document.Node) map[string]any {
opts["repeat"] = val.String() == "true" opts["repeat"] = val.String() == "true"
} }
if val, ok := node.Properties.Get("cooldown-ms"); ok { if val, ok := node.Properties.Get("cooldown-ms"); ok {
opts["cooldown-ms"] = val.String() if ms, err := strconv.Atoi(val.String()); err == nil {
opts["cooldown-ms"] = ms
}
} }
if val, ok := node.Properties.Get("allow-when-locked"); ok { if val, ok := node.Properties.Get("allow-when-locked"); ok {
opts["allow-when-locked"] = val.String() == "true" opts["allow-when-locked"] = val.String() == "true"
@@ -336,7 +340,14 @@ func (n *NiriProvider) buildBindNode(bind *overrideBind) *document.Node {
node.AddProperty("repeat", false, "") node.AddProperty("repeat", false, "")
} }
if v, ok := bind.Options["cooldown-ms"]; ok { if v, ok := bind.Options["cooldown-ms"]; ok {
node.AddProperty("cooldown-ms", v, "") switch val := v.(type) {
case int:
node.AddProperty("cooldown-ms", val, "")
case string:
if ms, err := strconv.Atoi(val); err == nil {
node.AddProperty("cooldown-ms", ms, "")
}
}
} }
if v, ok := bind.Options["allow-when-locked"]; ok && v == true { if v, ok := bind.Options["allow-when-locked"]; ok && v == true {
node.AddProperty("allow-when-locked", true, "") node.AddProperty("allow-when-locked", true, "")

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"github.com/sblinch/kdl-go" "github.com/sblinch/kdl-go"
@@ -17,6 +18,7 @@ type NiriKeyBinding struct {
Args []string Args []string
Description string Description string
HideOnOverlay bool HideOnOverlay bool
CooldownMs int
Source string Source string
} }
@@ -275,6 +277,7 @@ func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBin
var description string var description string
var hideOnOverlay bool var hideOnOverlay bool
var cooldownMs int
if node.Properties != nil { if node.Properties != nil {
if val, ok := node.Properties.Get("hotkey-overlay-title"); ok { if val, ok := node.Properties.Get("hotkey-overlay-title"); ok {
switch val.ValueString() { switch val.ValueString() {
@@ -284,6 +287,9 @@ func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBin
description = val.ValueString() description = val.ValueString()
} }
} }
if val, ok := node.Properties.Get("cooldown-ms"); ok {
cooldownMs, _ = strconv.Atoi(val.String())
}
} }
return &NiriKeyBinding{ return &NiriKeyBinding{
@@ -293,6 +299,7 @@ func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBin
Args: args, Args: args,
Description: description, Description: description,
HideOnOverlay: hideOnOverlay, HideOnOverlay: hideOnOverlay,
CooldownMs: cooldownMs,
Source: p.currentSource, Source: p.currentSource,
} }
} }

View File

@@ -7,6 +7,7 @@ type Keybind struct {
Subcategory string `json:"subcat,omitempty"` Subcategory string `json:"subcat,omitempty"`
Source string `json:"source,omitempty"` Source string `json:"source,omitempty"`
HideOnOverlay bool `json:"hideOnOverlay,omitempty"` HideOnOverlay bool `json:"hideOnOverlay,omitempty"`
CooldownMs int `json:"cooldownMs,omitempty"`
Conflict *Keybind `json:"conflict,omitempty"` Conflict *Keybind `json:"conflict,omitempty"`
} }

View File

@@ -643,6 +643,16 @@ func (m *Manager) applyCurrentTemp() {
return return
} }
m.configMutex.RLock()
low, high := m.config.LowTemp, m.config.HighTemp
m.configMutex.RUnlock()
if low == high {
m.applyGamma(low)
m.updateStateFromSchedule()
return
}
if !m.hasValidSchedule() { if !m.hasValidSchedule() {
m.updateStateFromSchedule() m.updateStateFromSchedule()
return return

View File

@@ -59,7 +59,7 @@ FloatingWindow {
title: I18n.tr("Settings", "settings window title") title: I18n.tr("Settings", "settings window title")
minimumSize: Qt.size(500, 400) minimumSize: Qt.size(500, 400)
implicitWidth: 800 implicitWidth: 800
implicitHeight: 940 implicitHeight: screen ? Math.min(940, screen.height - 100) : 940
color: Theme.surfaceContainer color: Theme.surfaceContainer
visible: false visible: false

View File

@@ -1088,6 +1088,7 @@ Item {
id: layoutComponent id: layoutComponent
DWLLayout { DWLLayout {
id: layoutWidget
layoutPopupVisible: layoutPopoutLoader.item ? layoutPopoutLoader.item.shouldBeVisible : false layoutPopupVisible: layoutPopoutLoader.item ? layoutPopoutLoader.item.shouldBeVisible : false
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
@@ -1100,14 +1101,19 @@ Item {
parentScreen: barWindow.screen parentScreen: barWindow.screen
onToggleLayoutPopup: { onToggleLayoutPopup: {
layoutPopoutLoader.active = true; layoutPopoutLoader.active = true;
if (!layoutPopoutLoader.item)
return;
const effectiveBarConfig = topBarContent.barConfig; const effectiveBarConfig = topBarContent.barConfig;
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1)); const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
if (layoutPopoutLoader.item && layoutPopoutLoader.item.setBarContext) {
layoutPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0); if (layoutPopoutLoader.item.setTriggerPosition) {
} const globalPos = layoutWidget.mapToGlobal(0, 0);
if (layoutPopoutLoader.item) { const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, layoutWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
PopoutManager.requestPopout(layoutPopoutLoader.item, undefined, "layout"); const widgetSection = topBarContent.getWidgetSection(parent) || "center";
layoutPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
} }
PopoutManager.requestPopout(layoutPopoutLoader.item, undefined, "layout");
} }
} }
} }

View File

@@ -1,4 +1,5 @@
import QtQuick import QtQuick
import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import Quickshell.Hyprland import Quickshell.Hyprland
@@ -245,11 +246,14 @@ Item {
if (!byApp[key]) { if (!byApp[key]) {
const moddedId = Paths.moddedAppId(keyBase); const moddedId = Paths.moddedAppId(keyBase);
const isSteamApp = moddedId.toLowerCase().includes("steam_app"); const isSteamApp = moddedId.toLowerCase().includes("steam_app");
const icon = isSteamApp ? "" : DesktopService.resolveIconPath(moddedId); const isQuickshell = keyBase === "org.quickshell";
const desktopEntry = DesktopEntries.heuristicLookup(keyBase);
const icon = isSteamApp ? "" : Paths.getAppIcon(keyBase, desktopEntry);
byApp[key] = { byApp[key] = {
"type": "icon", "type": "icon",
"icon": icon, "icon": icon,
"isSteamApp": isSteamApp, "isSteamApp": isSteamApp,
"isQuickshell": isQuickshell,
"active": !!((w.activated || w.is_focused) || (CompositorService.isNiri && w.is_focused)), "active": !!((w.activated || w.is_focused) || (CompositorService.isNiri && w.is_focused)),
"count": 1, "count": 1,
"windowId": w.address || w.id, "windowId": w.address || w.id,
@@ -446,6 +450,7 @@ Item {
readonly property real padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30)) readonly property real padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2) readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
readonly property real appIconSize: Theme.barIconSize(barThickness, -6)
function getRealWorkspaces() { function getRealWorkspaces() {
return root.workspaceList.filter(ws => { return root.workspaceList.filter(ws => {
@@ -719,14 +724,14 @@ Item {
readonly property real iconsExtraWidth: { readonly property real iconsExtraWidth: {
if (!root.isVertical && SettingsData.showWorkspaceApps && loadedIcons.length > 0) { if (!root.isVertical && SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons); const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
return numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0) + (isActive ? Theme.spacingXS : 0); return numIcons * root.appIconSize + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0) + (isActive ? Theme.spacingXS : 0);
} }
return 0; return 0;
} }
readonly property real iconsExtraHeight: { readonly property real iconsExtraHeight: {
if (root.isVertical && SettingsData.showWorkspaceApps && loadedIcons.length > 0) { if (root.isVertical && SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons); const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
return numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0) + (isActive ? Theme.spacingXS : 0); return numIcons * root.appIconSize + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0) + (isActive ? Theme.spacingXS : 0);
} }
return 0; return 0;
} }
@@ -897,7 +902,7 @@ Item {
Item { Item {
visible: loadedHasIcon && loadedIconData?.type === "icon" visible: loadedHasIcon && loadedIconData?.type === "icon"
width: wsIcon.width + (isActive && loadedIcons.length > 0 ? 4 : 0) width: wsIcon.width + (isActive && loadedIcons.length > 0 ? 4 : 0)
height: 18 height: root.appIconSize
DankIcon { DankIcon {
id: wsIcon id: wsIcon
@@ -912,7 +917,7 @@ Item {
Item { Item {
visible: loadedHasIcon && loadedIconData?.type === "text" visible: loadedHasIcon && loadedIconData?.type === "text"
width: wsText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0) width: wsText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0)
height: 18 height: root.appIconSize
StyledText { StyledText {
id: wsText id: wsText
@@ -927,7 +932,7 @@ Item {
Item { Item {
visible: SettingsData.showWorkspaceIndex && !loadedHasIcon visible: SettingsData.showWorkspaceIndex && !loadedHasIcon
width: wsIndexText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0) width: wsIndexText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0)
height: 18 height: root.appIconSize
StyledText { StyledText {
id: wsIndexText id: wsIndexText
@@ -944,48 +949,61 @@ Item {
values: loadedIcons.slice(0, SettingsData.maxWorkspaceIcons) values: loadedIcons.slice(0, SettingsData.maxWorkspaceIcons)
} }
delegate: Item { delegate: Item {
width: 18 width: root.appIconSize
height: 18 height: root.appIconSize
IconImage { IconImage {
id: appIcon id: rowAppIcon
property var windowId: modelData.windowId
anchors.fill: parent anchors.fill: parent
source: modelData.icon source: modelData.icon
opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6 opacity: modelData.active ? 1.0 : rowAppMouseArea.containsMouse ? 0.8 : 0.6
visible: !modelData.isSteamApp visible: !modelData.isSteamApp && !modelData.isQuickshell
}
IconImage {
anchors.fill: parent
source: modelData.icon
opacity: modelData.active ? 1.0 : rowAppMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isQuickshell
layer.enabled: true
layer.effect: MultiEffect {
saturation: 0
colorization: 1
colorizationColor: isActive ? Theme.primaryContainer : Theme.primary
}
} }
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
size: 18 size: root.appIconSize
name: "sports_esports" name: "sports_esports"
color: Theme.widgetTextColor color: Theme.widgetTextColor
opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6 opacity: modelData.active ? 1.0 : rowAppMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isSteamApp visible: modelData.isSteamApp
} }
MouseArea { MouseArea {
id: appMouseArea id: rowAppMouseArea
anchors.fill: parent anchors.fill: parent
enabled: isActive enabled: isActive
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (!appIcon.windowId) const winId = modelData.windowId;
if (!winId)
return; return;
if (CompositorService.isHyprland) { if (CompositorService.isHyprland) {
Hyprland.dispatch(`focuswindow address:${appIcon.windowId}`); Hyprland.dispatch(`focuswindow address:${winId}`);
} else if (CompositorService.isNiri) { } else if (CompositorService.isNiri) {
NiriService.focusWindow(appIcon.windowId); NiriService.focusWindow(winId);
} }
} }
} }
Rectangle { Rectangle {
visible: modelData.count > 1 && !isActive visible: modelData.count > 1 && !isActive
width: 12 width: root.appIconSize * 0.67
height: 12 height: root.appIconSize * 0.67
radius: 6 radius: root.appIconSize * 0.33
color: "black" color: "black"
border.color: "white" border.color: "white"
border.width: 1 border.width: 1
@@ -996,7 +1014,7 @@ Item {
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: modelData.count text: modelData.count
font.pixelSize: 8 font.pixelSize: root.appIconSize * 0.44
color: "white" color: "white"
} }
} }
@@ -1034,48 +1052,61 @@ Item {
values: loadedIcons.slice(0, SettingsData.maxWorkspaceIcons) values: loadedIcons.slice(0, SettingsData.maxWorkspaceIcons)
} }
delegate: Item { delegate: Item {
width: 18 width: root.appIconSize
height: 18 height: root.appIconSize
IconImage { IconImage {
id: appIcon id: colAppIcon
property var windowId: modelData.windowId
anchors.fill: parent anchors.fill: parent
source: modelData.icon source: modelData.icon
opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6 opacity: modelData.active ? 1.0 : colAppMouseArea.containsMouse ? 0.8 : 0.6
visible: !modelData.isSteamApp visible: !modelData.isSteamApp && !modelData.isQuickshell
}
IconImage {
anchors.fill: parent
source: modelData.icon
opacity: modelData.active ? 1.0 : colAppMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isQuickshell
layer.enabled: true
layer.effect: MultiEffect {
saturation: 0
colorization: 1
colorizationColor: isActive ? Theme.primaryContainer : Theme.primary
}
} }
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
size: 18 size: root.appIconSize
name: "sports_esports" name: "sports_esports"
color: Theme.widgetTextColor color: Theme.widgetTextColor
opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6 opacity: modelData.active ? 1.0 : colAppMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isSteamApp visible: modelData.isSteamApp
} }
MouseArea { MouseArea {
id: appMouseArea id: colAppMouseArea
anchors.fill: parent anchors.fill: parent
enabled: isActive enabled: isActive
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (!appIcon.windowId) const winId = modelData.windowId;
if (!winId)
return; return;
if (CompositorService.isHyprland) { if (CompositorService.isHyprland) {
Hyprland.dispatch(`focuswindow address:${appIcon.windowId}`); Hyprland.dispatch(`focuswindow address:${winId}`);
} else if (CompositorService.isNiri) { } else if (CompositorService.isNiri) {
NiriService.focusWindow(appIcon.windowId); NiriService.focusWindow(winId);
} }
} }
} }
Rectangle { Rectangle {
visible: modelData.count > 1 && !isActive visible: modelData.count > 1 && !isActive
width: 12 width: root.appIconSize * 0.67
height: 12 height: root.appIconSize * 0.67
radius: 6 radius: root.appIconSize * 0.33
color: "black" color: "black"
border.color: "white" border.color: "white"
border.width: 1 border.width: 1
@@ -1086,7 +1117,7 @@ Item {
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: modelData.count text: modelData.count
font.pixelSize: 8 font.pixelSize: root.appIconSize * 0.44
color: "white" color: "white"
} }
} }

View File

@@ -28,6 +28,7 @@ StyledRect {
property string pluginSettingsPath: pluginData ? (pluginData.settingsPath || "") : "" property string pluginSettingsPath: pluginData ? (pluginData.settingsPath || "") : ""
property var pluginPermissions: pluginData ? (pluginData.permissions || []) : [] property var pluginPermissions: pluginData ? (pluginData.permissions || []) : []
property bool hasSettings: pluginData && pluginData.settings !== undefined && pluginData.settings !== "" property bool hasSettings: pluginData && pluginData.settings !== undefined && pluginData.settings !== ""
property bool isSystemPlugin: pluginData ? (pluginData.source === "system") : false
property bool isExpanded: expandedPluginId === pluginId property bool isExpanded: expandedPluginId === pluginId
property bool isLoaded: { property bool isLoaded: {
PluginService.loadedPlugins; PluginService.loadedPlugins;
@@ -116,7 +117,7 @@ StyledRect {
height: 28 height: 28
radius: 14 radius: 14
color: updateArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) : "transparent" color: updateArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) : "transparent"
visible: DMSService.dmsAvailable && root.isLoaded && root.hasUpdate visible: DMSService.dmsAvailable && root.isLoaded && root.hasUpdate && !root.isSystemPlugin
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
@@ -160,7 +161,7 @@ StyledRect {
height: 28 height: 28
radius: 14 radius: 14
color: uninstallArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) : "transparent" color: uninstallArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) : "transparent"
visible: DMSService.dmsAvailable visible: DMSService.dmsAvailable && !root.isSystemPlugin
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent

View File

@@ -314,7 +314,8 @@ Singleton {
const keyData = { const keyData = {
key: bind.key || "", key: bind.key || "",
source: bind.source || "config", source: bind.source || "config",
isOverride: bind.source === "dms" isOverride: bind.source === "dms",
cooldownMs: bind.cooldownMs || 0
}; };
if (actionMap[action]) { if (actionMap[action]) {
actionMap[action].keys.push(keyData); actionMap[action].keys.push(keyData);
@@ -378,6 +379,8 @@ Singleton {
const cmd = ["dms", "keybinds", "set", currentProvider, bindData.key, bindData.action, "--desc", bindData.desc || ""]; const cmd = ["dms", "keybinds", "set", currentProvider, bindData.key, bindData.action, "--desc", bindData.desc || ""];
if (originalKey && originalKey !== bindData.key) if (originalKey && originalKey !== bindData.key)
cmd.push("--replace-key", originalKey); cmd.push("--replace-key", originalKey);
if (bindData.cooldownMs > 0)
cmd.push("--cooldown-ms", String(bindData.cooldownMs));
saveProcess.command = cmd; saveProcess.command = cmd;
saveProcess.running = true; saveProcess.running = true;
bindSaved(bindData.key); bindSaved(bindData.key);

View File

@@ -1 +1 @@
v1.0.0 v1.0.2

View File

@@ -23,6 +23,8 @@ Item {
property string editKey: "" property string editKey: ""
property string editAction: "" property string editAction: ""
property string editDesc: "" property string editDesc: ""
property int editCooldownMs: 0
property int _savedCooldownMs: -1
property bool hasChanges: false property bool hasChanges: false
property string _actionType: "" property string _actionType: ""
property bool addingNewKey: false property bool addingNewKey: false
@@ -90,6 +92,12 @@ Item {
editKey = keyToFind; editKey = keyToFind;
editAction = bindData.action || ""; editAction = bindData.action || "";
editDesc = bindData.desc || ""; editDesc = bindData.desc || "";
if (_savedCooldownMs >= 0) {
editCooldownMs = _savedCooldownMs;
_savedCooldownMs = -1;
} else {
editCooldownMs = keys[i].cooldownMs || 0;
}
hasChanges = false; hasChanges = false;
_actionType = Actions.getActionType(editAction); _actionType = Actions.getActionType(editAction);
useCustomCompositor = _actionType === "compositor" && editAction && !Actions.isKnownCompositorAction(editAction); useCustomCompositor = _actionType === "compositor" && editAction && !Actions.isKnownCompositorAction(editAction);
@@ -109,6 +117,7 @@ Item {
editKey = editingKeyIndex >= 0 ? keys[editingKeyIndex].key : ""; editKey = editingKeyIndex >= 0 ? keys[editingKeyIndex].key : "";
editAction = bindData.action || ""; editAction = bindData.action || "";
editDesc = bindData.desc || ""; editDesc = bindData.desc || "";
editCooldownMs = editingKeyIndex >= 0 ? (keys[editingKeyIndex].cooldownMs || 0) : 0;
hasChanges = false; hasChanges = false;
_actionType = Actions.getActionType(editAction); _actionType = Actions.getActionType(editAction);
useCustomCompositor = _actionType === "compositor" && editAction && !Actions.isKnownCompositorAction(editAction); useCustomCompositor = _actionType === "compositor" && editAction && !Actions.isKnownCompositorAction(editAction);
@@ -127,6 +136,7 @@ Item {
addingNewKey = false; addingNewKey = false;
editingKeyIndex = index; editingKeyIndex = index;
editKey = keys[index].key; editKey = keys[index].key;
editCooldownMs = keys[index].cooldownMs || 0;
hasChanges = false; hasChanges = false;
} }
@@ -137,8 +147,11 @@ Item {
editAction = changes.action; editAction = changes.action;
if (changes.desc !== undefined) if (changes.desc !== undefined)
editDesc = changes.desc; editDesc = changes.desc;
if (changes.cooldownMs !== undefined)
editCooldownMs = changes.cooldownMs;
const origKey = editingKeyIndex >= 0 && editingKeyIndex < keys.length ? keys[editingKeyIndex].key : ""; const origKey = editingKeyIndex >= 0 && editingKeyIndex < keys.length ? keys[editingKeyIndex].key : "";
hasChanges = editKey !== origKey || editAction !== (bindData.action || "") || editDesc !== (bindData.desc || ""); const origCooldown = editingKeyIndex >= 0 && editingKeyIndex < keys.length ? (keys[editingKeyIndex].cooldownMs || 0) : 0;
hasChanges = editKey !== origKey || editAction !== (bindData.action || "") || editDesc !== (bindData.desc || "") || editCooldownMs !== origCooldown;
} }
function canSave() { function canSave() {
@@ -156,10 +169,12 @@ Item {
let desc = editDesc; let desc = editDesc;
if (expandedLoader.item?.currentTitle !== undefined) if (expandedLoader.item?.currentTitle !== undefined)
desc = expandedLoader.item.currentTitle; desc = expandedLoader.item.currentTitle;
_savedCooldownMs = editCooldownMs;
saveBind(origKey, { saveBind(origKey, {
key: editKey, key: editKey,
action: editAction, action: editAction,
desc: desc desc: desc,
cooldownMs: editCooldownMs
}); });
hasChanges = false; hasChanges = false;
addingNewKey = false; addingNewKey = false;
@@ -1431,6 +1446,57 @@ Item {
} }
} }
RowLayout {
Layout.fillWidth: true
spacing: Theme.spacingM
StyledText {
text: I18n.tr("Cooldown")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceVariantText
Layout.preferredWidth: 60
}
DankTextField {
id: cooldownField
Layout.preferredWidth: 100
Layout.preferredHeight: 40
placeholderText: "0"
Connections {
target: root
function onEditCooldownMsChanged() {
const newText = root.editCooldownMs > 0 ? String(root.editCooldownMs) : "";
if (cooldownField.text !== newText)
cooldownField.text = newText;
}
}
Component.onCompleted: {
text = root.editCooldownMs > 0 ? String(root.editCooldownMs) : "";
}
onTextChanged: {
const val = parseInt(text) || 0;
if (val !== root.editCooldownMs)
root.updateEdit({
cooldownMs: val
});
}
}
StyledText {
text: I18n.tr("ms")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
Item {
Layout.fillWidth: true
}
}
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 1 Layout.preferredHeight: 1