1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-12 23:32:50 -04:00

Compare commits

..

8 Commits

Author SHA1 Message Date
bbedward 3d35af2a87 cc: fix plugin reloading in bar position changes 2026-02-17 17:24:22 -05:00
bbedward fed3c36f84 popout: anchor height changing popout surfaces to top and bottom 2026-02-17 17:18:07 -05:00
bbedward 414d81aa40 workspaces: fix named workspace icons 2026-02-17 16:02:13 -05:00
bbedward d548803769 dankinstall: no_anim on dms layers 2026-02-17 15:32:08 -05:00
bbedward 1180258394 system updater: fix hide no update option 2026-02-17 13:53:17 -05:00
bbedward 48a566a24b launcher: fix kb navigation not always showing last delegate in view 2026-02-17 13:07:43 -05:00
bbedward 3bc5d1df81 doctor: add qt6-imageformats check 2026-02-17 12:58:09 -05:00
bbedward c7222e2e86 bump version, codename, disable changelog 2026-02-17 12:02:36 -05:00
111 changed files with 4009 additions and 7283 deletions
+17 -50
View File
@@ -8,31 +8,31 @@ body:
value: |
## DankMaterialShell Bug Report
Limit your report to one issue per submission unless closely related
- type: dropdown
- type: checkboxes
id: compositor
attributes:
label: Compositor
options:
- Niri
- Hyprland
- MangoWC (dwl)
- Sway
- label: Niri
- label: Hyprland
- label: MangoWC (dwl)
- label: Sway
validations:
required: true
- type: dropdown
- type: checkboxes
id: distribution
attributes:
label: Distribution
options:
- Arch Linux
- CachyOS
- Fedora
- NixOS
- Debian
- Ubuntu
- Gentoo
- OpenSUSE
- Other (specify below)
- label: Arch Linux
- label: CachyOS
- label: Fedora
- label: NixOS
- label: Debian
- label: Ubuntu
- label: Gentoo
- label: OpenSUSE
- label: Other (specify below)
validations:
required: true
- type: input
@@ -42,45 +42,12 @@ body:
placeholder: e.g., PikaOS, Void Linux, etc.
validations:
required: false
- type: dropdown
id: installation_method
attributes:
label: Select your Installation Method
options:
- DankInstaller
- Distro Packaging
- Source
validations:
required: true
- type: dropdown
id: original_installation_method_different
attributes:
label: Was your original Installation method different?
options:
- "Yes"
- No (specify below)
default: 0
validations:
required: false
- type: input
id: original_installation_method_specify
attributes:
label: If no, specify
placeholder: e.g., Distro Packaging, then Source
validations:
required: false
- type: textarea
id: dms_doctor
attributes:
label: dms doctor -vC
description: Output of `dms doctor -vC` command — paste between the lines below to keep it collapsed in the issue
description: Output of `dms doctor -vC` command
placeholder: Paste the output of `dms doctor -vC` here
value: |
<details>
<summary>Click to expand</summary>
</details>
validations:
required: true
- type: textarea
@@ -102,7 +69,7 @@ body:
- type: textarea
id: steps_to_reproduce
attributes:
label: Steps to Reproduce
label: Steps to Reproduce & Installation Method
description: Please provide detailed steps to reproduce the issue
placeholder: |
1. ...
+7 -14
View File
@@ -23,25 +23,18 @@ body:
placeholder: Why is this feature important?
validations:
required: false
- type: dropdown
- type: checkboxes
id: compositor
attributes:
label: Compositor(s)
description: Is this feature specific to one or more compositors?
options:
- All compositors
- Niri
- Hyprland
- MangoWC (dwl)
- Sway
- Other (specify below)
validations:
required: true
- type: input
id: compositor_other
attributes:
label: If Other, please specify
placeholder: e.g., Wayfire, Mutter, etc.
- label: All compositors
- label: Niri
- label: Hyprland
- label: MangoWC (dwl)
- label: Sway
- label: Other (specify below)
validations:
required: false
- type: textarea
+10 -65
View File
@@ -7,87 +7,32 @@ body:
attributes:
value: |
## DankMaterialShell Support Request
- type: dropdown
- type: checkboxes
id: compositor
attributes:
label: Compositor
options:
- Niri
- Hyprland
- MangoWC (dwl)
- Sway
- Other (specify below)
validations:
required: true
- type: input
id: compositor_other
attributes:
label: If Other, please specify
placeholder: e.g., Wayfire, Mutter, etc.
- label: Niri
- label: Hyprland
- label: MangoWC (dwl)
- label: Sway
- label: Other (specify below)
validations:
required: false
- type: dropdown
- type: input
id: distribution
attributes:
label: Distribution
options:
- Arch Linux
- CachyOS
- Fedora
- NixOS
- Debian
- Ubuntu
- Gentoo
- OpenSUSE
- Other (specify below)
validations:
required: true
- type: input
id: distribution_other
attributes:
label: If Other, please specify
placeholder: e.g., PikaOS, Void Linux, etc.
validations:
required: false
- type: dropdown
id: installation_method
attributes:
label: Select your Installation Method
options:
- DankInstaller
- Distro Packaging
- Source
validations:
required: true
- type: dropdown
id: original_installation_method_different
attributes:
label: Was your original Installation method different?
options:
- "Yes"
- No (specify below)
default: 0
validations:
required: false
- type: input
id: original_installation_method_specify
attributes:
label: If no, specify
placeholder: e.g., Distro Packaging, then Source
description: Which Linux distribution are you using? (e.g., Arch, Fedora, Debian, etc.)
placeholder: Your Linux distribution
validations:
required: false
- type: textarea
id: dms_doctor
attributes:
label: dms doctor -vC
description: Output of `dms doctor -vC` command — paste between the lines below to keep it collapsed in the issue
description: Output of `dms doctor -vC` command
placeholder: Paste the output of `dms doctor -vC` here
value: |
<details>
<summary>Click to expand</summary>
</details>
validations:
required: false
- type: textarea
+383
View File
@@ -0,0 +1,383 @@
name: Update OBS Packages
on:
workflow_dispatch:
inputs:
package:
description: "Package to update (dms, dms-git, or all)"
required: false
default: "all"
force_upload:
description: "Force upload without version check"
required: false
default: "false"
type: choice
options:
- "false"
- "true"
rebuild_release:
description: "Release number for rebuilds (e.g., 2, 3, 4 to increment spec Release)"
required: false
default: ""
push:
tags:
- "v*"
schedule:
- cron: "0 */3 * * *" # Every 3 hours for dms-git builds
jobs:
check-updates:
name: Check for updates
runs-on: ubuntu-latest
outputs:
has_updates: ${{ steps.check.outputs.has_updates }}
packages: ${{ steps.check.outputs.packages }}
version: ${{ steps.check.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- 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: Check for updates
id: check
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
echo "packages=dms" >> $GITHUB_OUTPUT
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "Triggered by tag: $VERSION (always update)"
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "packages=dms-git" >> $GITHUB_OUTPUT
echo "Checking if dms-git source has changed..."
# Get current commit hash (8 chars to match spec format)
CURRENT_COMMIT=$(git rev-parse --short=8 HEAD)
# Check OBS for last uploaded commit
OBS_BASE="$HOME/.cache/osc-checkouts"
mkdir -p "$OBS_BASE"
OBS_PROJECT="home:AvengeMedia:dms-git"
if [[ -d "$OBS_BASE/$OBS_PROJECT/dms-git" ]]; then
cd "$OBS_BASE/$OBS_PROJECT/dms-git"
osc up -q 2>/dev/null || true
# Extract commit hash from spec Version line & format like; 0.6.2+git2264.a679be68
if [[ -f "dms-git.spec" ]]; then
OBS_COMMIT=$(grep "^Version:" "dms-git.spec" | grep -oP '\.[a-f0-9]{8}' | tr -d '.' || echo "")
if [[ -n "$OBS_COMMIT" ]]; then
if [[ "$CURRENT_COMMIT" == "$OBS_COMMIT" ]]; then
echo "has_updates=false" >> $GITHUB_OUTPUT
echo "📋 Commit $CURRENT_COMMIT already uploaded to OBS, skipping"
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 New commit detected: $CURRENT_COMMIT (OBS has $OBS_COMMIT)"
fi
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 Could not extract OBS commit, proceeding with update"
fi
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 No spec file in OBS, proceeding with update"
fi
cd "${{ github.workspace }}"
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 First upload to OBS, update needed"
fi
elif [[ "${{ github.event.inputs.force_upload }}" == "true" ]]; then
PKG="${{ github.event.inputs.package }}"
if [[ -z "$PKG" || "$PKG" == "all" ]]; then
echo "packages=all" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "🚀 Force upload: all packages"
else
echo "packages=$PKG" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "🚀 Force upload: $PKG"
fi
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "Manual trigger: ${{ github.event.inputs.package }}"
else
echo "packages=all" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
fi
update-obs:
name: Upload to OBS
needs: check-updates
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
if: |
github.event.inputs.force_upload == 'true' ||
github.event_name == 'workflow_dispatch' ||
needs.check-updates.outputs.has_updates == 'true'
steps:
- name: Generate GitHub App Token
id: generate_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.generate_token.outputs.token }}
- name: Check if last commit was automated
id: check-loop
run: |
LAST_COMMIT_MSG=$(git log -1 --pretty=%B | head -1)
if [[ "$LAST_COMMIT_MSG" == "ci: Auto-update PPA packages"* ]] || [[ "$LAST_COMMIT_MSG" == "ci: Auto-update OBS packages"* ]]; then
echo "⏭️ Last commit was automated ($LAST_COMMIT_MSG), skipping to prevent infinite loop"
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "✅ Last commit was not automated, proceeding"
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: Determine packages to update
if: steps.check-loop.outputs.skip != 'true'
id: packages
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
echo "packages=dms" >> $GITHUB_OUTPUT
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Triggered by tag: $VERSION"
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
echo "Triggered by schedule: updating git package"
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
echo "Manual trigger: ${{ github.event.inputs.package }}"
else
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
fi
- name: Update dms-git spec version
if: steps.check-loop.outputs.skip != 'true' && (contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all')
run: |
# Get commit info for dms-git versioning
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
COMMIT_COUNT=$(git rev-list --count HEAD)
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.2")
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
echo "📦 Updating dms-git.spec to version: $NEW_VERSION"
# Update version in spec
sed -i "s/^Version:.*/Version: $NEW_VERSION/" distro/opensuse/dms-git.spec
# Add changelog entry
DATE_STR=$(date "+%a %b %d %Y")
CHANGELOG_ENTRY="* $DATE_STR Avenge Media <AvengeMedia.US@gmail.com> - ${NEW_VERSION}-1\n- Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)"
sed -i "/%changelog/a\\$CHANGELOG_ENTRY" distro/opensuse/dms-git.spec
- name: Update Debian dms-git changelog version
if: steps.check-loop.outputs.skip != 'true' && (contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all')
run: |
# Get commit info for dms-git versioning
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
COMMIT_COUNT=$(git rev-list --count HEAD)
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.2")
# Debian version format: 0.6.2+git2256.9162e314
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
echo "📦 Updating Debian dms-git changelog to version: $NEW_VERSION"
CHANGELOG_DATE=$(date -R)
CHANGELOG_FILE="distro/debian/dms-git/debian/changelog"
# Get current version from changelog
CURRENT_VERSION=$(head -1 "$CHANGELOG_FILE" | sed 's/.*(\([^)]*\)).*/\1/')
echo "Current Debian version: $CURRENT_VERSION"
echo "New version: $NEW_VERSION"
# Only update if version changed
if [ "$CURRENT_VERSION" != "$NEW_VERSION" ]; then
# Create new changelog entry at top
TEMP_CHANGELOG=$(mktemp)
cat > "$TEMP_CHANGELOG" << EOF
dms-git ($NEW_VERSION) nightly; urgency=medium
* Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)
-- Avenge Media <AvengeMedia.US@gmail.com> $CHANGELOG_DATE
EOF
# Prepend to existing changelog
cat "$CHANGELOG_FILE" >> "$TEMP_CHANGELOG"
mv "$TEMP_CHANGELOG" "$CHANGELOG_FILE"
echo "✓ Updated Debian changelog: $CURRENT_VERSION → $NEW_VERSION"
else
echo "✓ Debian changelog already at version $NEW_VERSION"
fi
- name: Update dms stable version
if: steps.check-loop.outputs.skip != 'true' && steps.packages.outputs.version != ''
run: |
VERSION="${{ steps.packages.outputs.version }}"
VERSION_NO_V="${VERSION#v}"
echo "Updating packaging to version $VERSION_NO_V"
# Update openSUSE dms spec (stable only)
sed -i "s/^Version:.*/Version: $VERSION_NO_V/" distro/opensuse/dms.spec
# Update openSUSE spec changelog
DATE_STR=$(date "+%a %b %d %Y")
CHANGELOG_ENTRY="* $DATE_STR AvengeMedia <maintainer@avengemedia.com> - ${VERSION_NO_V}-1\\n- Update to stable $VERSION release\\n- Bug fixes and improvements"
sed -i "/%changelog/a\\$CHANGELOG_ENTRY\\n" distro/opensuse/dms.spec
# Update Debian _service files (both tar_scm and download_url formats)
for service in distro/debian/*/_service; do
if [[ -f "$service" ]]; then
# Update tar_scm revision parameter (for dms-git)
sed -i "s|<param name=\"revision\">v[0-9.]*</param>|<param name=\"revision\">$VERSION</param>|" "$service"
# Update download_url paths (for dms stable)
sed -i "s|/v[0-9.]\+/|/$VERSION/|g" "$service"
sed -i "s|/tags/v[0-9.]\+\.tar\.gz|/tags/$VERSION.tar.gz|g" "$service"
fi
done
# Update Debian changelog for dms stable
if [[ -f "distro/debian/dms/debian/changelog" ]]; then
CHANGELOG_DATE=$(date -R)
TEMP_CHANGELOG=$(mktemp)
cat > "$TEMP_CHANGELOG" << EOF
dms ($VERSION_NO_V) stable; urgency=medium
* Update to $VERSION stable release
* Bug fixes and improvements
-- Avenge Media <AvengeMedia.US@gmail.com> $CHANGELOG_DATE
EOF
cat "distro/debian/dms/debian/changelog" >> "$TEMP_CHANGELOG"
mv "$TEMP_CHANGELOG" "distro/debian/dms/debian/changelog"
echo "✓ Updated Debian changelog to $VERSION_NO_V"
fi
- name: Install Go
if: steps.check-loop.outputs.skip != 'true'
uses: actions/setup-go@v5
with:
go-version: "1.24"
- name: Install OSC
if: steps.check-loop.outputs.skip != 'true'
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: Upload to OBS
if: steps.check-loop.outputs.skip != 'true'
env:
FORCE_UPLOAD: ${{ github.event.inputs.force_upload }}
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
run: |
PACKAGES="${{ steps.packages.outputs.packages }}"
MESSAGE="Automated update from GitHub Actions"
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
MESSAGE="Update to ${{ steps.packages.outputs.version }}"
fi
if [[ "$PACKAGES" == "all" ]]; then
bash distro/scripts/obs-upload.sh dms "$MESSAGE"
bash distro/scripts/obs-upload.sh dms-git "Automated git update"
else
bash distro/scripts/obs-upload.sh "$PACKAGES" "$MESSAGE"
fi
- name: Get changed packages
if: steps.check-loop.outputs.skip != 'true'
id: changed-packages
run: |
# Check if there are any changes to commit
if git diff --exit-code distro/debian/ distro/opensuse/ >/dev/null 2>&1; then
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "📋 No changelog or spec changes to commit"
else
echo "has_changes=true" >> $GITHUB_OUTPUT
# Get list of changed packages for commit message
CHANGED_DEB=$(git diff --name-only distro/debian/ 2>/dev/null | grep 'debian/changelog' | xargs dirname 2>/dev/null | xargs dirname 2>/dev/null | xargs basename 2>/dev/null | tr '\n' ', ' | sed 's/, $//' || echo "")
CHANGED_SUSE=$(git diff --name-only distro/opensuse/ 2>/dev/null | grep '\.spec$' | sed 's|distro/opensuse/||' | sed 's/\.spec$//' | tr '\n' ', ' | sed 's/, $//' || echo "")
PKGS=$(echo "$CHANGED_DEB,$CHANGED_SUSE" | tr ',' '\n' | grep -v '^$' | sort -u | tr '\n' ',' | sed 's/,$//')
echo "packages=$PKGS" >> $GITHUB_OUTPUT
echo "📋 Changed packages: $PKGS"
fi
- name: Commit packaging changes
if: steps.check-loop.outputs.skip != 'true' && steps.changed-packages.outputs.has_changes == 'true'
run: |
git config user.name "dms-ci[bot]"
git config user.email "dms-ci[bot]@users.noreply.github.com"
git add distro/debian/*/debian/changelog distro/opensuse/*.spec
git commit -m "ci: Auto-update OBS packages [${{ steps.changed-packages.outputs.packages }}]" -m "🤖 Automated by GitHub Actions"
git pull --rebase origin master
git push
- name: Summary
run: |
echo "### OBS Package Update Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.check-updates.outputs.has_updates }}" == "false" ]]; then
echo "- **Status**: Skipped (no changes detected)" >> $GITHUB_STEP_SUMMARY
fi
echo "- **Project**: https://build.opensuse.org/project/show/home:AvengeMedia" >> $GITHUB_STEP_SUMMARY
+298
View File
@@ -0,0 +1,298 @@
name: Update PPA Packages
on:
workflow_dispatch:
inputs:
package:
description: "Package to upload (dms, dms-git, dms-greeter, or all)"
required: false
default: "dms-git"
force_upload:
description: "Force upload without version check"
required: false
default: "false"
type: choice
options:
- "false"
- "true"
rebuild_release:
description: "Release number for rebuilds (e.g., 2, 3, 4 for ppa2, ppa3, ppa4)"
required: false
default: ""
schedule:
- cron: "0 */3 * * *" # Every 3 hours for dms-git builds
jobs:
check-updates:
name: Check for updates
runs-on: ubuntu-latest
outputs:
has_updates: ${{ steps.check.outputs.has_updates }}
packages: ${{ steps.check.outputs.packages }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for updates
id: check
run: |
if [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "packages=dms-git" >> $GITHUB_OUTPUT
echo "Checking if dms-git source has changed..."
# Get current commit hash (8 chars to match changelog format)
CURRENT_COMMIT=$(git rev-parse --short=8 HEAD)
# Extract commit hash from changelog
# Format: dms-git (0.6.2+git2264.c5c5ce84) questing; urgency=medium
CHANGELOG_FILE="distro/ubuntu/dms-git/debian/changelog"
if [[ -f "$CHANGELOG_FILE" ]]; then
CHANGELOG_COMMIT=$(head -1 "$CHANGELOG_FILE" | grep -oP '\.[a-f0-9]{8}' | tr -d '.' || echo "")
if [[ -n "$CHANGELOG_COMMIT" ]]; then
if [[ "$CURRENT_COMMIT" == "$CHANGELOG_COMMIT" ]]; then
echo "has_updates=false" >> $GITHUB_OUTPUT
echo "📋 Commit $CURRENT_COMMIT already in changelog, skipping upload"
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 New commit detected: $CURRENT_COMMIT (changelog has $CHANGELOG_COMMIT)"
fi
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 Could not extract commit from changelog, proceeding with upload"
fi
else
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "📋 No changelog file found, proceeding with upload"
fi
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "Manual trigger: ${{ github.event.inputs.package }}"
else
echo "packages=dms-git" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
fi
upload-ppa:
name: Upload to PPA
needs: check-updates
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
if: |
github.event.inputs.force_upload == 'true' ||
github.event_name == 'workflow_dispatch' ||
needs.check-updates.outputs.has_updates == 'true'
steps:
- name: Generate GitHub App Token
id: generate_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.generate_token.outputs.token }}
- name: Check if last commit was automated
id: check-loop
run: |
LAST_COMMIT_MSG=$(git log -1 --pretty=%B | head -1)
if [[ "$LAST_COMMIT_MSG" == "ci: Auto-update PPA packages"* ]] || [[ "$LAST_COMMIT_MSG" == "ci: Auto-update OBS packages"* ]]; then
echo "⏭️ Last commit was automated ($LAST_COMMIT_MSG), skipping to prevent infinite loop"
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "✅ Last commit was not automated, proceeding"
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: Set up Go
if: steps.check-loop.outputs.skip != 'true'
uses: actions/setup-go@v5
with:
go-version: "1.24"
cache: false
- name: Install build dependencies
if: steps.check-loop.outputs.skip != 'true'
run: |
sudo apt-get update
sudo apt-get install -y \
debhelper \
devscripts \
dput \
lftp \
build-essential \
fakeroot \
dpkg-dev
- name: Configure GPG
if: steps.check-loop.outputs.skip != 'true'
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: Determine packages to upload
if: steps.check-loop.outputs.skip != 'true'
id: packages
run: |
if [[ "${{ github.event.inputs.force_upload }}" == "true" ]]; then
PKG="${{ github.event.inputs.package }}"
if [[ -z "$PKG" || "$PKG" == "all" ]]; then
echo "packages=all" >> $GITHUB_OUTPUT
echo "🚀 Force upload: all packages"
else
echo "packages=$PKG" >> $GITHUB_OUTPUT
echo "🚀 Force upload: $PKG"
fi
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
echo "Triggered by schedule: uploading git package"
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
# Manual package selection should respect change detection
SELECTED_PKG="${{ github.event.inputs.package }}"
UPDATED_PKG="${{ needs.check-updates.outputs.packages }}"
# Check if manually selected package is in the updated list
if [[ "$UPDATED_PKG" == *"$SELECTED_PKG"* ]] || [[ "$SELECTED_PKG" == "all" ]]; then
echo "packages=$SELECTED_PKG" >> $GITHUB_OUTPUT
echo "📦 Manual selection (has updates): $SELECTED_PKG"
else
echo "packages=" >> $GITHUB_OUTPUT
echo "⚠️ Manual selection '$SELECTED_PKG' has no updates - skipping (use force_upload to override)"
fi
else
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
fi
- name: Upload to PPA
if: steps.check-loop.outputs.skip != 'true'
run: |
PACKAGES="${{ steps.packages.outputs.packages }}"
REBUILD_RELEASE="${{ github.event.inputs.rebuild_release }}"
if [[ -z "$PACKAGES" ]]; then
echo "No packages selected for upload. Skipping."
exit 0
fi
# Build command arguments
BUILD_ARGS=()
if [[ -n "$REBUILD_RELEASE" ]]; then
BUILD_ARGS+=("$REBUILD_RELEASE")
echo "✓ Using rebuild release number: ppa$REBUILD_RELEASE"
fi
if [[ "$PACKAGES" == "all" ]]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Uploading dms to PPA..."
if [ -n "$REBUILD_RELEASE" ]; then
echo "🔄 Using rebuild release number: ppa$REBUILD_RELEASE"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
bash distro/scripts/ppa-upload.sh dms dms questing "${BUILD_ARGS[@]}"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Uploading dms-git to PPA..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
bash distro/scripts/ppa-upload.sh dms-git dms-git questing "${BUILD_ARGS[@]}"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Uploading dms-greeter to PPA..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
bash distro/scripts/ppa-upload.sh dms-greeter danklinux questing "${BUILD_ARGS[@]}"
else
# Map package to PPA name
case "$PACKAGES" in
dms)
PPA_NAME="dms"
;;
dms-git)
PPA_NAME="dms-git"
;;
dms-greeter)
PPA_NAME="danklinux"
;;
*)
PPA_NAME="$PACKAGES"
;;
esac
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Uploading $PACKAGES to PPA..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
bash distro/scripts/ppa-upload.sh "$PACKAGES" "$PPA_NAME" questing "${BUILD_ARGS[@]}"
fi
- name: Get changed packages
if: steps.check-loop.outputs.skip != 'true'
id: changed-packages
run: |
# Check if there are any changelog changes to commit
if git diff --exit-code distro/ubuntu/ >/dev/null 2>&1; then
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "📋 No changelog changes to commit"
else
echo "has_changes=true" >> $GITHUB_OUTPUT
# Get list of changed packages for commit message (deduplicate)
CHANGED=$(git diff --name-only distro/ubuntu/ | grep 'debian/changelog' | sed 's|/debian/changelog||' | xargs -I{} basename {} | sort -u | tr '\n' ',' | sed 's/,$//')
echo "packages=$CHANGED" >> $GITHUB_OUTPUT
echo "📋 Changed packages: $CHANGED"
echo "📋 Debug - Changed files:"
git diff --name-only distro/ubuntu/ | grep 'debian/changelog' || echo "No changelog files found"
fi
- name: Commit changelog changes
if: steps.check-loop.outputs.skip != 'true' && steps.changed-packages.outputs.has_changes == 'true'
run: |
git config user.name "dms-ci[bot]"
git config user.email "dms-ci[bot]@users.noreply.github.com"
git add distro/ubuntu/*/debian/changelog
git commit -m "ci: Auto-update PPA packages [${{ steps.changed-packages.outputs.packages }}]" -m "🤖 Automated by GitHub Actions"
git pull --rebase origin master
git push
- name: Summary
run: |
echo "### PPA Package Upload Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
if [[ "${{ needs.check-updates.outputs.has_updates }}" == "false" ]]; then
echo "- **Status**: Skipped (no changes detected)" >> $GITHUB_STEP_SUMMARY
fi
PACKAGES="${{ steps.packages.outputs.packages }}"
if [[ "$PACKAGES" == "all" ]]; then
echo "- **PPA dms**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
echo "- **PPA dms-git**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
echo "- **PPA danklinux**: https://launchpad.net/~avengemedia/+archive/ubuntu/danklinux/+packages" >> $GITHUB_STEP_SUMMARY
elif [[ "$PACKAGES" == "dms" ]]; then
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
elif [[ "$PACKAGES" == "dms-git" ]]; then
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
elif [[ "$PACKAGES" == "dms-greeter" ]]; then
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/danklinux/+packages" >> $GITHUB_STEP_SUMMARY
fi
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "Builds will appear once Launchpad processes the uploads." >> $GITHUB_STEP_SUMMARY
+3 -5
View File
@@ -5,13 +5,11 @@ repos:
- id: trailing-whitespace
- id: check-yaml
- id: end-of-file-fixer
- repo: local
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.10.0.1
hooks:
- id: shellcheck
name: shellcheck
entry: shellcheck -e SC2164 -e SC2001 -e SC2012 -e SC2317
language: system
types: [shell]
args: [-e, SC2164, -e, SC2001, -e, SC2012, -e, SC2317]
- repo: local
hooks:
- id: go-mod-tidy
+1 -1
View File
@@ -22,7 +22,7 @@ nix develop
This will provide:
- Go 1.25+ toolchain (go, gopls, delve, go-tools) and GNU Make
- Go 1.24 toolchain (go, gopls, delve, go-tools) and GNU Make
- Quickshell and required QML packages
- Properly configured QML2_IMPORT_PATH
+1 -1
View File
@@ -96,7 +96,7 @@ The on-screen preview displays the selected format. JSON output includes hex, RG
## Building
Requires Go 1.25+
Requires Go 1.24+
**Development build:**
+13 -15
View File
@@ -13,16 +13,16 @@ import (
)
var (
ssOutputName string
ssCursor string
ssFormat string
ssQuality int
ssOutputDir string
ssFilename string
ssNoClipboard bool
ssNoFile bool
ssNoNotify bool
ssStdout bool
ssOutputName string
ssIncludeCursor bool
ssFormat string
ssQuality int
ssOutputDir string
ssFilename string
ssNoClipboard bool
ssNoFile bool
ssNoNotify bool
ssStdout bool
)
var screenshotCmd = &cobra.Command{
@@ -52,7 +52,7 @@ Examples:
dms screenshot last # Last region (pre-selected)
dms screenshot --no-clipboard # Save file only
dms screenshot --no-file # Clipboard only
dms screenshot --cursor=on # Include cursor
dms screenshot --cursor # Include cursor
dms screenshot -f jpg -q 85 # JPEG with quality 85`,
}
@@ -111,7 +111,7 @@ var notifyActionCmd = &cobra.Command{
func init() {
screenshotCmd.PersistentFlags().StringVarP(&ssOutputName, "output", "o", "", "Output name for 'output' mode")
screenshotCmd.PersistentFlags().StringVar(&ssCursor, "cursor", "off", "Include cursor in screenshot (on/off)")
screenshotCmd.PersistentFlags().BoolVar(&ssIncludeCursor, "cursor", false, "Include cursor in screenshot")
screenshotCmd.PersistentFlags().StringVarP(&ssFormat, "format", "f", "png", "Output format (png, jpg, ppm)")
screenshotCmd.PersistentFlags().IntVarP(&ssQuality, "quality", "q", 90, "JPEG quality (1-100)")
screenshotCmd.PersistentFlags().StringVarP(&ssOutputDir, "dir", "d", "", "Output directory")
@@ -136,9 +136,7 @@ func getScreenshotConfig(mode screenshot.Mode) screenshot.Config {
config := screenshot.DefaultConfig()
config.Mode = mode
config.OutputName = ssOutputName
if strings.EqualFold(ssCursor, "on") {
config.Cursor = screenshot.CursorOn
}
config.IncludeCursor = ssIncludeCursor
config.Clipboard = !ssNoClipboard
config.SaveFile = !ssNoFile
config.Notify = !ssNoNotify
+1 -1
View File
@@ -100,7 +100,7 @@ windowrule = float on, match:class ^(blueman-manager)$
windowrule = float on, match:class ^(org\.gnome\.Nautilus)$
windowrule = float on, match:class ^(xdg-desktop-portal)$
windowrule = no_initial_focus on, match:class ^(steam)$, match:title ^(notificationtoasts)
windowrule = noinitialfocus on, match:class ^(steam)$, match:title ^(notificationtoasts)
windowrule = pin on, match:class ^(steam)$, match:title ^(notificationtoasts)
windowrule = float on, match:class ^(firefox)$, match:title ^(Picture-in-Picture)$
-3
View File
@@ -26,9 +26,6 @@ func init() {
Register("cachyos", "#08A283", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
return NewArchDistribution(config, logChan)
})
Register("catos", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
return NewArchDistribution(config, logChan)
})
Register("endeavouros", "#7F3FBF", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
return NewArchDistribution(config, logChan)
})
+1 -1
View File
@@ -430,7 +430,7 @@ func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []Packa
}
// Add repository
repoLine := fmt.Sprintf("deb [signed-by=%s arch=%s] %s/ /", keyringPath, runtime.GOARCH, baseURL)
repoLine := fmt.Sprintf("deb [signed-by=%s, arch=%s] %s/ /", keyringPath, runtime.GOARCH, baseURL)
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
-2
View File
@@ -341,8 +341,6 @@ func (n *NiriProvider) buildActionFromNode(bindNode *document.Node) string {
val := arg.ValueString()
if val == "" {
parts = append(parts, `""`)
} else if strings.ContainsAny(val, " \t") {
parts = append(parts, `"`+strings.ReplaceAll(val, `"`, `\"`)+`"`)
} else {
parts = append(parts, val)
}
+50 -142
View File
@@ -33,7 +33,6 @@ const (
TemplateKindTerminal
TemplateKindGTK
TemplateKindVSCode
TemplateKindEmacs
)
type TemplateDef struct {
@@ -66,7 +65,7 @@ var templateRegistry = []TemplateDef{
{ID: "dgop", Commands: []string{"dgop"}, ConfigFile: "dgop.toml"},
{ID: "kcolorscheme", ConfigFile: "kcolorscheme.toml", RunUnconditionally: true},
{ID: "vscode", Kind: TemplateKindVSCode},
{ID: "emacs", Commands: []string{"emacs"}, ConfigFile: "emacs.toml", Kind: TemplateKindEmacs},
{ID: "emacs", Commands: []string{"emacs"}, ConfigFile: "emacs.toml"},
}
func (c *ColorMode) GTKTheme() string {
@@ -79,8 +78,7 @@ func (c *ColorMode) GTKTheme() string {
}
var (
matugenVersionMu sync.Mutex
matugenVersionOK bool
matugenVersionOnce sync.Once
matugenSupportsCOE bool
matugenIsV4 bool
)
@@ -336,10 +334,6 @@ output_path = '%s'
appendVSCodeConfig(cfgFile, "cursor", filepath.Join(homeDir, ".cursor/extensions"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "windsurf", filepath.Join(homeDir, ".windsurf/extensions"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "vscode-insiders", filepath.Join(homeDir, ".vscode-insiders/extensions"), opts.ShellDir)
case TemplateKindEmacs:
if utils.EmacsConfigDir() != "" {
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
}
default:
appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile)
}
@@ -497,9 +491,6 @@ func substituteVars(content, shellDir string) string {
result = strings.ReplaceAll(result, "'CONFIG_DIR/", "'"+utils.XDGConfigHome()+"/")
result = strings.ReplaceAll(result, "'DATA_DIR/", "'"+utils.XDGDataHome()+"/")
result = strings.ReplaceAll(result, "'CACHE_DIR/", "'"+utils.XDGCacheHome()+"/")
if emacsDir := utils.EmacsConfigDir(); emacsDir != "" {
result = strings.ReplaceAll(result, "'EMACS_DIR/", "'"+emacsDir+"/")
}
return result
}
@@ -520,159 +511,78 @@ func extractTOMLSection(content, startMarker, endMarker string) string {
return content[startIdx : startIdx+endIdx]
}
type matugenFlags struct {
supportsCOE bool
isV4 bool
func checkMatugenVersion() {
matugenVersionOnce.Do(func() {
cmd := exec.Command("matugen", "--version")
output, err := cmd.Output()
if err != nil {
return
}
versionStr := strings.TrimSpace(string(output))
versionStr = strings.TrimPrefix(versionStr, "matugen ")
parts := strings.Split(versionStr, ".")
if len(parts) < 2 {
return
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return
}
matugenSupportsCOE = major > 3 || (major == 3 && minor >= 1)
matugenIsV4 = major >= 4
if matugenSupportsCOE {
log.Infof("Matugen %s supports --continue-on-error", versionStr)
}
if matugenIsV4 {
log.Infof("Matugen %s: using v4 flags", versionStr)
}
})
}
func detectMatugenVersion() (matugenFlags, error) {
matugenVersionMu.Lock()
defer matugenVersionMu.Unlock()
if matugenVersionOK {
return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil
}
return detectMatugenVersionLocked()
}
func redetectMatugenVersion(old matugenFlags) (matugenFlags, bool) {
matugenVersionMu.Lock()
defer matugenVersionMu.Unlock()
matugenVersionOK = false
flags, err := detectMatugenVersionLocked()
if err != nil {
return old, false
}
changed := flags.supportsCOE != old.supportsCOE || flags.isV4 != old.isV4
return flags, changed
}
func detectMatugenVersionLocked() (matugenFlags, error) {
cmd := exec.Command("matugen", "--version")
output, err := cmd.Output()
if err != nil {
return matugenFlags{}, fmt.Errorf("failed to get matugen version: %w", err)
}
versionStr := strings.TrimSpace(string(output))
versionStr = strings.TrimPrefix(versionStr, "matugen ")
parts := strings.Split(versionStr, ".")
if len(parts) < 2 {
return matugenFlags{}, fmt.Errorf("unexpected matugen version format: %q", versionStr)
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return matugenFlags{}, fmt.Errorf("failed to parse matugen major version %q: %w", parts[0], err)
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return matugenFlags{}, fmt.Errorf("failed to parse matugen minor version %q: %w", parts[1], err)
}
matugenSupportsCOE = major > 3 || (major == 3 && minor >= 1)
matugenIsV4 = major >= 4
matugenVersionOK = true
func runMatugen(args []string) error {
checkMatugenVersion()
if matugenSupportsCOE {
log.Infof("Matugen %s supports --continue-on-error", versionStr)
args = append([]string{"--continue-on-error"}, args...)
}
if matugenIsV4 {
log.Infof("Matugen %s: using v4 flags", versionStr)
}
return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil
}
func buildMatugenArgs(baseArgs []string, flags matugenFlags) []string {
args := make([]string, 0, len(baseArgs)+4)
if flags.supportsCOE {
args = append(args, "--continue-on-error")
}
args = append(args, baseArgs...)
if flags.isV4 {
args = append(args, "--source-color-index", "0")
}
return args
}
func runMatugen(baseArgs []string) error {
flags, err := detectMatugenVersion()
if err != nil {
return err
}
args := buildMatugenArgs(baseArgs, flags)
cmd := exec.Command("matugen", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
runErr := cmd.Run()
if runErr == nil {
return nil
}
log.Warnf("Matugen failed (v4=%v): %v", flags.isV4, runErr)
newFlags, changed := redetectMatugenVersion(flags)
if !changed {
return runErr
}
log.Warnf("Matugen version changed (v4: %v -> %v), retrying", flags.isV4, newFlags.isV4)
args = buildMatugenArgs(baseArgs, newFlags)
retryCmd := exec.Command("matugen", args...)
retryCmd.Stdout = os.Stdout
retryCmd.Stderr = os.Stderr
return retryCmd.Run()
return cmd.Run()
}
func runMatugenDryRun(opts *Options) (string, error) {
flags, err := detectMatugenVersion()
if err != nil {
return "", err
}
checkMatugenVersion()
output, dryErr := execDryRun(opts, flags)
if dryErr == nil {
return output, nil
}
log.Warnf("Matugen dry-run failed (v4=%v): %v", flags.isV4, dryErr)
newFlags, changed := redetectMatugenVersion(flags)
if !changed {
return "", dryErr
}
log.Warnf("Matugen version changed (v4: %v -> %v), retrying dry-run", flags.isV4, newFlags.isV4)
return execDryRun(opts, newFlags)
}
func execDryRun(opts *Options, flags matugenFlags) (string, error) {
var baseArgs []string
var args []string
switch opts.Kind {
case "hex":
baseArgs = []string{"color", "hex", opts.Value}
args = []string{"color", "hex", opts.Value}
default:
baseArgs = []string{opts.Kind, opts.Value}
args = []string{opts.Kind, opts.Value}
}
baseArgs = append(baseArgs, "-m", "dark", "-t", opts.MatugenType, "--json", "hex", "--dry-run")
if flags.isV4 {
baseArgs = append(baseArgs, "--source-color-index", "0", "--old-json-output")
args = append(args, "-m", "dark", "-t", opts.MatugenType, "--json", "hex", "--dry-run")
if matugenIsV4 {
args = append(args, "--source-color-index", "0", "--old-json-output")
}
cmd := exec.Command("matugen", baseArgs...)
var stderr strings.Builder
cmd.Stderr = &stderr
cmd := exec.Command("matugen", args...)
output, err := cmd.Output()
if err != nil {
if stderr.Len() > 0 {
return "", fmt.Errorf("matugen %v failed (v4=%v): %s", baseArgs, flags.isV4, strings.TrimSpace(stderr.String()))
}
return "", fmt.Errorf("matugen %v failed (v4=%v): %w", baseArgs, flags.isV4, err)
return "", err
}
return strings.ReplaceAll(string(output), "\n", ""), nil
}
@@ -909,8 +819,6 @@ func CheckTemplates(checker utils.AppChecker) []TemplateCheck {
detected = true
case tmpl.Kind == TemplateKindVSCode:
detected = checkVSCodeExtension(homeDir)
case tmpl.Kind == TemplateKindEmacs:
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks) && utils.EmacsConfigDir() != ""
default:
detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks)
}
@@ -258,7 +258,7 @@ func (i *ExtWorkspaceManagerV1) Dispatch(opcode uint32, fd int, data []byte) {
l := 0
objectID := client.Uint32(data[l : l+4])
proxy := i.Context().GetProxy(objectID)
if proxy != nil && !proxy.IsZombie() {
if proxy != nil {
e.WorkspaceGroup = proxy.(*ExtWorkspaceGroupHandleV1)
} else {
groupHandle := &ExtWorkspaceGroupHandleV1{}
@@ -278,7 +278,7 @@ func (i *ExtWorkspaceManagerV1) Dispatch(opcode uint32, fd int, data []byte) {
l := 0
objectID := client.Uint32(data[l : l+4])
proxy := i.Context().GetProxy(objectID)
if proxy != nil && !proxy.IsZombie() {
if proxy != nil {
e.Workspace = proxy.(*ExtWorkspaceHandleV1)
} else {
wsHandle := &ExtWorkspaceHandleV1{}
+1 -1
View File
@@ -108,7 +108,7 @@ func NewRegionSelector(s *Screenshoter) *RegionSelector {
screenshoter: s,
outputs: make(map[uint32]*WaylandOutput),
preCapture: make(map[*WaylandOutput]*PreCapture),
showCapturedCursor: s.config.Cursor == CursorOn,
showCapturedCursor: true,
}
}
+8 -2
View File
@@ -453,7 +453,10 @@ func (s *Screenshoter) blitBuffer(dst, src *ShmBuffer, dstX, dstY int, yInverted
}
func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult, error) {
cursor := int32(s.config.Cursor)
cursor := int32(0)
if s.config.IncludeCursor {
cursor = 1
}
frame, err := s.screencopy.CaptureOutput(cursor, output.wlOutput)
if err != nil {
@@ -621,7 +624,10 @@ func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Regio
}
}
cursor := int32(s.config.Cursor)
cursor := int32(0)
if s.config.IncludeCursor {
cursor = 1
}
frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h)
if err != nil {
+20 -27
View File
@@ -19,13 +19,6 @@ const (
FormatPPM
)
type CursorMode int
const (
CursorOff CursorMode = iota
CursorOn
)
type Region struct {
X int32 `json:"x"`
Y int32 `json:"y"`
@@ -49,29 +42,29 @@ type Output struct {
}
type Config struct {
Mode Mode
OutputName string
Cursor CursorMode
Format Format
Quality int
OutputDir string
Filename string
Clipboard bool
SaveFile bool
Notify bool
Stdout bool
Mode Mode
OutputName string
IncludeCursor bool
Format Format
Quality int
OutputDir string
Filename string
Clipboard bool
SaveFile bool
Notify bool
Stdout bool
}
func DefaultConfig() Config {
return Config{
Mode: ModeRegion,
Cursor: CursorOff,
Format: FormatPNG,
Quality: 90,
OutputDir: "",
Filename: "",
Clipboard: true,
SaveFile: true,
Notify: true,
Mode: ModeRegion,
IncludeCursor: false,
Format: FormatPNG,
Quality: 90,
OutputDir: "",
Filename: "",
Clipboard: true,
SaveFile: true,
Notify: true,
}
}
+6 -24
View File
@@ -232,15 +232,8 @@ func (m *Manager) setupDataDeviceSync() {
return
}
prevOffer := m.currentOffer
m.currentOffer = offer
if prevOffer != nil && prevOffer != offer {
m.offerMutex.Lock()
delete(m.offerMimeTypes, prevOffer)
m.offerMutex.Unlock()
}
m.offerMutex.RLock()
mimes := m.offerMimeTypes[offer]
m.offerMutex.RUnlock()
@@ -594,26 +587,20 @@ func (m *Manager) uriListPreview(data []byte) (string, bool) {
uris = strings.Split(text, "\n")
}
if len(uris) > 1 {
return fmt.Sprintf("[[ %d files ]]", len(uris)), false
}
if len(uris) == 1 && strings.HasPrefix(uris[0], "file://") {
filePath := strings.TrimPrefix(uris[0], "file://")
info, err := os.Stat(filePath)
if err != nil || info.IsDir() {
return m.textPreview(data), false
}
cfg := m.getConfig()
if info.Size() <= cfg.MaxEntrySize {
if info, err := os.Stat(filePath); err == nil && !info.IsDir() {
if imgData, err := os.ReadFile(filePath); err == nil {
if config, imgFmt, err := image.DecodeConfig(bytes.NewReader(imgData)); err == nil {
return fmt.Sprintf("[[ file %s %s %dx%d ]]", filepath.Base(filePath), imgFmt, config.Width, config.Height), true
}
}
return fmt.Sprintf("[[ file %s ]]", filepath.Base(filePath)), false
}
return fmt.Sprintf("[[ file %s ]]", filepath.Base(filePath)), false
}
if len(uris) > 1 {
return fmt.Sprintf("[[ %d files ]]", len(uris)), false
}
return m.textPreview(data), false
@@ -636,11 +623,6 @@ func (m *Manager) tryReadImageFromURI(data []byte) ([]byte, string, bool) {
return nil, "", false
}
cfg := m.getConfig()
if info.Size() > cfg.MaxEntrySize {
return nil, "", false
}
imgData, err := os.ReadFile(filePath)
if err != nil {
return nil, "", false
@@ -16,8 +16,4 @@ const (
dbusScreensaverPath = "/ScreenSaver"
dbusScreensaverPath2 = "/org/freedesktop/ScreenSaver"
dbusScreensaverInterface = "org.freedesktop.ScreenSaver"
dbusGnomeScreensaverName = "org.gnome.ScreenSaver"
dbusGnomeScreensaverPath = "/org/gnome/ScreenSaver"
dbusGnomeScreensaverInterface = "org.gnome.ScreenSaver"
)
@@ -191,12 +191,6 @@ func (m *Manager) Close() {
return true
})
m.screensaverSubscribers.Range(func(key string, ch chan ScreensaverState) bool {
close(ch)
m.screensaverSubscribers.Delete(key)
return true
})
if m.systemConn != nil {
m.systemConn.Close()
}
+58 -137
View File
@@ -1,7 +1,6 @@
package freedesktop
import (
"fmt"
"path/filepath"
"strings"
"sync/atomic"
@@ -16,9 +15,45 @@ type screensaverHandler struct {
manager *Manager
}
func screensaverIntrospectIface(ifaceName string) introspect.Interface {
return introspect.Interface{
Name: ifaceName,
func (m *Manager) initializeScreensaver() error {
if m.sessionConn == nil {
m.stateMutex.Lock()
m.state.Screensaver.Available = false
m.stateMutex.Unlock()
return nil
}
reply, err := m.sessionConn.RequestName(dbusScreensaverName, dbus.NameFlagDoNotQueue)
if err != nil {
log.Warnf("Failed to request screensaver name: %v", err)
m.stateMutex.Lock()
m.state.Screensaver.Available = false
m.stateMutex.Unlock()
return nil
}
if reply != dbus.RequestNameReplyPrimaryOwner {
log.Warnf("Screensaver name already owned by another process")
m.stateMutex.Lock()
m.state.Screensaver.Available = false
m.stateMutex.Unlock()
return nil
}
handler := &screensaverHandler{manager: m}
if err := m.sessionConn.Export(handler, dbusScreensaverPath, dbusScreensaverInterface); err != nil {
log.Warnf("Failed to export screensaver on %s: %v", dbusScreensaverPath, err)
return nil
}
if err := m.sessionConn.Export(handler, dbusScreensaverPath2, dbusScreensaverInterface); err != nil {
log.Warnf("Failed to export screensaver on %s: %v", dbusScreensaverPath2, err)
return nil
}
screensaverIface := introspect.Interface{
Name: dbusScreensaverInterface,
Methods: []introspect.Method{
{
Name: "Inhibit",
@@ -34,106 +69,40 @@ func screensaverIntrospectIface(ifaceName string) introspect.Interface {
{Name: "cookie", Type: "u", Direction: "in"},
},
},
{
Name: "GetActive",
Args: []introspect.Arg{
{Name: "active", Type: "b", Direction: "out"},
},
},
{
Name: "SetActive",
Args: []introspect.Arg{
{Name: "active", Type: "b", Direction: "in"},
},
},
{
Name: "Lock",
},
},
Signals: []introspect.Signal{
{
Name: "ActiveChanged",
Args: []introspect.Arg{
{Name: "new_value", Type: "b"},
},
},
},
}
}
func (m *Manager) initializeScreensaver() error {
if m.sessionConn == nil {
m.stateMutex.Lock()
m.state.Screensaver.Available = false
m.stateMutex.Unlock()
return nil
introNode := &introspect.Node{
Name: dbusScreensaverPath,
Interfaces: []introspect.Interface{
introspect.IntrospectData,
screensaverIface,
},
}
if err := m.sessionConn.Export(introspect.NewIntrospectable(introNode), dbusScreensaverPath, "org.freedesktop.DBus.Introspectable"); err != nil {
log.Warnf("Failed to export introspectable on %s: %v", dbusScreensaverPath, err)
}
handler := &screensaverHandler{manager: m}
m.screensaverFreedesktopClaimed = m.claimScreensaverName(handler,
dbusScreensaverName, dbusScreensaverInterface, dbusScreensaverPath, dbusScreensaverPath2)
m.screensaverGnomeClaimed = m.claimScreensaverName(handler,
dbusGnomeScreensaverName, dbusGnomeScreensaverInterface, dbusGnomeScreensaverPath)
if !m.screensaverFreedesktopClaimed && !m.screensaverGnomeClaimed {
log.Warn("No screensaver interface could be claimed")
m.stateMutex.Lock()
m.state.Screensaver.Available = false
m.stateMutex.Unlock()
return nil
introNode2 := &introspect.Node{
Name: dbusScreensaverPath2,
Interfaces: []introspect.Interface{
introspect.IntrospectData,
screensaverIface,
},
}
if err := m.sessionConn.Export(introspect.NewIntrospectable(introNode2), dbusScreensaverPath2, "org.freedesktop.DBus.Introspectable"); err != nil {
log.Warnf("Failed to export introspectable on %s: %v", dbusScreensaverPath2, err)
}
go m.watchPeerDisconnects()
m.stateMutex.Lock()
m.state.Screensaver.Available = true
m.state.Screensaver.Active = false
m.state.Screensaver.Inhibited = false
m.state.Screensaver.Inhibitors = []ScreensaverInhibitor{}
m.stateMutex.Unlock()
log.Info("Screensaver listener initialized")
return nil
}
func (m *Manager) claimScreensaverName(handler *screensaverHandler, name, iface string, paths ...dbus.ObjectPath) bool {
reply, err := m.sessionConn.RequestName(name, dbus.NameFlagDoNotQueue)
if err != nil {
log.Warnf("Failed to request screensaver name %s: %v", name, err)
return false
}
if reply != dbus.RequestNameReplyPrimaryOwner {
log.Warnf("Screensaver name %s already owned by another process", name)
return false
}
if err := m.exportScreensaverOnPaths(handler, iface, paths...); err != nil {
log.Warnf("Failed to export screensaver on %s: %v", name, err)
return false
}
log.Infof("Claimed %s on session bus", name)
return true
}
// exportScreensaverOnPaths exports the handler and introspection on the given
// paths under the specified interface name.
func (m *Manager) exportScreensaverOnPaths(handler *screensaverHandler, ifaceName string, paths ...dbus.ObjectPath) error {
iface := screensaverIntrospectIface(ifaceName)
for _, path := range paths {
if err := m.sessionConn.Export(handler, path, ifaceName); err != nil {
return fmt.Errorf("export handler on %s: %w", path, err)
}
node := &introspect.Node{
Name: string(path),
Interfaces: []introspect.Interface{
introspect.IntrospectData,
iface,
},
}
if err := m.sessionConn.Export(introspect.NewIntrospectable(node), path, "org.freedesktop.DBus.Introspectable"); err != nil {
log.Warnf("Failed to export introspectable on %s: %v", path, err)
}
}
log.Info("Screensaver inhibit listener initialized")
return nil
}
@@ -299,51 +268,3 @@ func (m *Manager) NotifyScreensaverSubscribers() {
return true
})
}
func (h *screensaverHandler) GetActive() (bool, *dbus.Error) {
h.manager.stateMutex.RLock()
active := h.manager.state.Screensaver.Active
h.manager.stateMutex.RUnlock()
return active, nil
}
func (h *screensaverHandler) SetActive(active bool) *dbus.Error {
h.manager.SetScreenLockActive(active)
return nil
}
func (h *screensaverHandler) Lock() *dbus.Error {
h.manager.SetScreenLockActive(true)
return nil
}
func (m *Manager) SetScreenLockActive(active bool) {
m.stateMutex.Lock()
changed := m.state.Screensaver.Active != active
m.state.Screensaver.Active = active
m.stateMutex.Unlock()
if !changed {
return
}
log.Infof("Screen lock active changed: %v", active)
defer m.NotifyScreensaverSubscribers()
if m.sessionConn == nil {
return
}
if m.screensaverFreedesktopClaimed {
if err := m.sessionConn.Emit(dbusScreensaverPath, dbusScreensaverInterface+".ActiveChanged", active); err != nil {
log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusScreensaverPath, err)
}
if err := m.sessionConn.Emit(dbusScreensaverPath2, dbusScreensaverInterface+".ActiveChanged", active); err != nil {
log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusScreensaverPath2, err)
}
}
if m.screensaverGnomeClaimed {
if err := m.sessionConn.Emit(dbusGnomeScreensaverPath, dbusGnomeScreensaverInterface+".ActiveChanged", active); err != nil {
log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusGnomeScreensaverPath, err)
}
}
}
@@ -1,102 +0,0 @@
package freedesktop
import (
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestSetScreenLockActive_ChangesState(t *testing.T) {
manager := &Manager{
state: &FreedeskState{
Screensaver: ScreensaverState{Available: true},
},
stateMutex: sync.RWMutex{},
}
assert.False(t, manager.GetScreensaverState().Active)
manager.SetScreenLockActive(true)
assert.True(t, manager.GetScreensaverState().Active)
manager.SetScreenLockActive(false)
assert.False(t, manager.GetScreensaverState().Active)
}
func TestSetScreenLockActive_NoChangeNoDuplicate(t *testing.T) {
ch := make(chan ScreensaverState, 64)
manager := &Manager{
state: &FreedeskState{
Screensaver: ScreensaverState{Available: true, Active: false},
},
stateMutex: sync.RWMutex{},
}
manager.screensaverSubscribers.Store("test", ch)
defer manager.screensaverSubscribers.Delete("test")
// Setting to same value should not notify
manager.SetScreenLockActive(false)
select {
case <-ch:
t.Fatal("should not have received notification for no-change")
case <-time.After(50 * time.Millisecond):
// Expected: no notification
}
}
func TestSetScreenLockActive_NotifiesSubscribers(t *testing.T) {
ch := make(chan ScreensaverState, 64)
manager := &Manager{
state: &FreedeskState{
Screensaver: ScreensaverState{Available: true, Active: false},
},
stateMutex: sync.RWMutex{},
}
manager.screensaverSubscribers.Store("test", ch)
defer manager.screensaverSubscribers.Delete("test")
manager.SetScreenLockActive(true)
select {
case state := <-ch:
assert.True(t, state.Active)
case <-time.After(time.Second):
t.Fatal("timeout waiting for subscriber notification")
}
}
func TestSetScreenLockActive_NilSessionConn(t *testing.T) {
manager := &Manager{
state: &FreedeskState{
Screensaver: ScreensaverState{Available: true},
},
stateMutex: sync.RWMutex{},
}
assert.NotPanics(t, func() {
manager.SetScreenLockActive(true)
})
assert.True(t, manager.GetScreensaverState().Active)
}
func TestGetActive_ReturnsCurrentState(t *testing.T) {
manager := &Manager{
state: &FreedeskState{
Screensaver: ScreensaverState{Available: true, Active: true},
},
stateMutex: sync.RWMutex{},
}
handler := &screensaverHandler{manager: manager}
active, dbusErr := handler.GetActive()
assert.Nil(t, dbusErr)
assert.True(t, active)
}
func TestScreensaverState_ActiveDefaultsFalse(t *testing.T) {
state := ScreensaverState{}
assert.False(t, state.Active)
}
+10 -13
View File
@@ -39,7 +39,6 @@ type ScreensaverInhibitor struct {
type ScreensaverState struct {
Available bool `json:"available"`
Active bool `json:"active"`
Inhibited bool `json:"inhibited"`
Inhibitors []ScreensaverInhibitor `json:"inhibitors"`
}
@@ -51,16 +50,14 @@ type FreedeskState struct {
}
type Manager struct {
state *FreedeskState
stateMutex sync.RWMutex
systemConn *dbus.Conn
sessionConn *dbus.Conn
accountsObj dbus.BusObject
settingsObj dbus.BusObject
currentUID uint64
subscribers syncmap.Map[string, chan FreedeskState]
screensaverSubscribers syncmap.Map[string, chan ScreensaverState]
screensaverCookieCounter uint32
screensaverFreedesktopClaimed bool
screensaverGnomeClaimed bool
state *FreedeskState
stateMutex sync.RWMutex
systemConn *dbus.Conn
sessionConn *dbus.Conn
accountsObj dbus.BusObject
settingsObj dbus.BusObject
currentUID uint64
subscribers syncmap.Map[string, chan FreedeskState]
screensaverSubscribers syncmap.Map[string, chan ScreensaverState]
screensaverCookieCounter uint32
}
-30
View File
@@ -1516,11 +1516,7 @@ func Start(printDocs bool) error {
}
}()
loginctlReady := make(chan struct{})
freedesktopReady := make(chan struct{})
go func() {
defer close(loginctlReady)
if err := InitializeLoginctlManager(); err != nil {
log.Warnf("Loginctl manager unavailable: %v", err)
} else {
@@ -1529,7 +1525,6 @@ func Start(printDocs bool) error {
}()
go func() {
defer close(freedesktopReady)
if err := InitializeFreedeskManager(); err != nil {
log.Warnf("Freedesktop manager unavailable: %v", err)
} else if freedesktopManager != nil {
@@ -1538,31 +1533,6 @@ func Start(printDocs bool) error {
}
}()
// Bridge loginctl lock state to the freedesktop/gnome screensaver
// ActiveChanged signal so apps like Bitwarden can detect screen lock.
go func() {
<-loginctlReady
<-freedesktopReady
if loginctlManager == nil || freedesktopManager == nil {
return
}
ch := loginctlManager.Subscribe("dms-lock-bridge")
defer loginctlManager.Unsubscribe("dms-lock-bridge")
initial := loginctlManager.GetState()
lastLocked := initial.Locked
freedesktopManager.SetScreenLockActive(lastLocked)
for state := range ch {
if state.Locked != lastLocked {
lastLocked = state.Locked
freedesktopManager.SetScreenLockActive(lastLocked)
}
}
}()
if err := InitializeWaylandManager(); err != nil {
log.Warnf("Wayland manager unavailable: %v", err)
}
+1 -1
View File
@@ -162,7 +162,7 @@ func TestCleanupStaleSockets(t *testing.T) {
tempDir := t.TempDir()
t.Setenv("XDG_RUNTIME_DIR", tempDir)
staleSocket := filepath.Join(tempDir, "danklinux-4194305.sock")
staleSocket := filepath.Join(tempDir, "danklinux-999999.sock")
err := os.WriteFile(staleSocket, []byte{}, 0o600)
require.NoError(t, err)
-16
View File
@@ -38,22 +38,6 @@ func XDGConfigHome() string {
return filepath.Join(home, ".config")
}
func EmacsConfigDir() string {
home, _ := os.UserHomeDir()
emacsD := filepath.Join(home, ".emacs.d")
if info, err := os.Stat(emacsD); err == nil && info.IsDir() {
return emacsD
}
xdgEmacs := filepath.Join(XDGConfigHome(), "emacs")
if info, err := os.Stat(xdgEmacs); err == nil && info.IsDir() {
return xdgEmacs
}
return ""
}
func ExpandPath(path string) (string, error) {
expanded := os.ExpandEnv(path)
expanded = filepath.Clean(expanded)
+1 -2
View File
@@ -181,7 +181,7 @@
buildInputs =
with pkgs;
[
go_1_25
go_1_24
gopls
delve
go-tools
@@ -189,7 +189,6 @@
prek
uv # for prek
shellcheck
# Nix development tools
nixd
+1 -1
View File
@@ -1 +1 @@
Saffron Bloom
The Wolverine
-2
View File
@@ -142,8 +142,6 @@ Singleton {
if (tabIndex !== undefined && popout.currentTabIndex !== undefined) {
popout.currentTabIndex = tabIndex;
}
if (popout.updateSurfacePosition)
popout.updateSurfacePosition();
currentPopoutTriggers[screenName] = triggerId;
return;
}
+1 -9
View File
@@ -60,7 +60,6 @@ Singleton {
property bool _hasLoaded: false
property bool _isReadOnly: false
property bool _hasUnsavedChanges: false
property bool _selfWrite: false
property var _loadedSettingsSnapshot: null
property var pluginSettings: ({})
property var builtInPluginSettings: ({})
@@ -1244,7 +1243,6 @@ Singleton {
function saveSettings() {
if (_loading || _parseError || !_hasLoaded)
return;
_selfWrite = true;
settingsFile.setText(JSON.stringify(Store.toJson(root), null, 2));
if (_isReadOnly)
_checkSettingsWritable();
@@ -2591,13 +2589,7 @@ Singleton {
blockWrites: true
atomicWrites: true
watchChanges: true
onFileChanged: {
if (_selfWrite) {
_selfWrite = false;
return;
}
settingsFileReloadDebounce.restart();
}
onFileChanged: settingsFileReloadDebounce.restart()
onLoaded: {
if (isGreeterMode)
return;
-64
View File
@@ -859,70 +859,6 @@ Item {
return success ? `WIDGET_TOGGLE_SUCCESS: ${widgetId}` : `WIDGET_TOGGLE_FAILED: ${widgetId}`;
}
function openWith(widgetId: string, mode: string): string {
if (!widgetId)
return "ERROR: No widget ID specified";
if (!BarWidgetService.hasWidget(widgetId))
return `WIDGET_NOT_FOUND: ${widgetId}`;
const widget = BarWidgetService.getWidgetOnFocusedScreen(widgetId);
if (!widget)
return `WIDGET_NOT_AVAILABLE: ${widgetId}`;
if (typeof widget.openWithMode !== "function")
return `WIDGET_OPEN_WITH_NOT_SUPPORTED: ${widgetId}`;
widget.openWithMode(mode || "all");
return `WIDGET_OPEN_WITH_SUCCESS: ${widgetId} ${mode}`;
}
function toggleWith(widgetId: string, mode: string): string {
if (!widgetId)
return "ERROR: No widget ID specified";
if (!BarWidgetService.hasWidget(widgetId))
return `WIDGET_NOT_FOUND: ${widgetId}`;
const widget = BarWidgetService.getWidgetOnFocusedScreen(widgetId);
if (!widget)
return `WIDGET_NOT_AVAILABLE: ${widgetId}`;
if (typeof widget.toggleWithMode !== "function")
return `WIDGET_TOGGLE_WITH_NOT_SUPPORTED: ${widgetId}`;
widget.toggleWithMode(mode || "all");
return `WIDGET_TOGGLE_WITH_SUCCESS: ${widgetId} ${mode}`;
}
function openQuery(widgetId: string, query: string): string {
if (!widgetId)
return "ERROR: No widget ID specified";
if (!BarWidgetService.hasWidget(widgetId))
return `WIDGET_NOT_FOUND: ${widgetId}`;
const widget = BarWidgetService.getWidgetOnFocusedScreen(widgetId);
if (!widget)
return `WIDGET_NOT_AVAILABLE: ${widgetId}`;
if (typeof widget.openWithQuery !== "function")
return `WIDGET_OPEN_QUERY_NOT_SUPPORTED: ${widgetId}`;
widget.openWithQuery(query || "");
return `WIDGET_OPEN_QUERY_SUCCESS: ${widgetId}`;
}
function toggleQuery(widgetId: string, query: string): string {
if (!widgetId)
return "ERROR: No widget ID specified";
if (!BarWidgetService.hasWidget(widgetId))
return `WIDGET_NOT_FOUND: ${widgetId}`;
const widget = BarWidgetService.getWidgetOnFocusedScreen(widgetId);
if (!widget)
return `WIDGET_NOT_AVAILABLE: ${widgetId}`;
if (typeof widget.toggleWithQuery !== "function")
return `WIDGET_TOGGLE_QUERY_NOT_SUPPORTED: ${widgetId}`;
widget.toggleWithQuery(query || "");
return `WIDGET_TOGGLE_QUERY_SUCCESS: ${widgetId}`;
}
function list(): string {
const widgets = BarWidgetService.getRegisteredWidgetIds();
if (widgets.length === 0)
@@ -18,7 +18,7 @@ FloatingWindow {
}
objectName: "changelogModal"
title: i18n("What's New")
title: "What's New"
minimumSize: Qt.size(modalWidth, modalHeight)
maximumSize: Qt.size(modalWidth, modalHeight)
color: Theme.surfaceContainer
@@ -81,7 +81,7 @@ FloatingWindow {
onClicked: root.dismiss()
DankTooltip {
text: i18n("Close")
text: "Close"
}
}
}
@@ -125,7 +125,7 @@ FloatingWindow {
spacing: Theme.spacingM
DankButton {
text: i18n("Read Full Release Notes")
text: "Read Full Release Notes"
iconName: "open_in_new"
backgroundColor: Theme.surfaceContainerHighest
textColor: Theme.surfaceText
@@ -133,7 +133,7 @@ FloatingWindow {
}
DankButton {
text: i18n("Got It")
text: "Got It"
iconName: "check"
backgroundColor: Theme.primary
textColor: Theme.primaryText
@@ -12,7 +12,7 @@ Singleton {
readonly property int popoutWidth: 550
readonly property int popoutHeight: 500
readonly property int itemHeight: 72
readonly property int thumbnailSize: 100
readonly property int thumbnailSize: 48
readonly property int retryInterval: 50
readonly property int viewportBuffer: 100
readonly property int extendedBuffer: 200
@@ -84,8 +84,7 @@ Rectangle {
anchors.right: actionButtons.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
// height: contentColumn.implicitHeight
height: ClipboardConstants.itemHeight
height: contentColumn.implicitHeight
clip: true
ClipboardThumbnail {
@@ -93,7 +92,7 @@ Rectangle {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize
height: entryType === "image" ? ClipboardConstants.itemHeight - 4 : Theme.iconSize // 100 - 4 = 96, 96:72 = 4:3
height: entryType === "image" ? ClipboardConstants.thumbnailSize : Theme.iconSize
entry: root.entry
entryType: root.entryType
modal: root.modal
@@ -137,23 +137,23 @@ Item {
anchors.margins: 2
source: thumbnailImage
maskEnabled: true
maskSource: clipboardRoundedRectangularMask
maskSource: clipboardCircularMask
visible: entryType === "image" && thumbnailImage.status === Image.Ready && thumbnailImage.source != ""
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
Item {
id: clipboardRoundedRectangularMask
width: ClipboardConstants.thumbnailSize
height: ClipboardConstants.itemHeight - 4
id: clipboardCircularMask
width: ClipboardConstants.thumbnailSize - 4
height: ClipboardConstants.thumbnailSize - 4
layer.enabled: true
layer.smooth: true
visible: false
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius / 2 // Thumbnail corner radius is divided by 2 so it doesnt look weird on large corner radius (eg: 32px)
radius: width / 2
color: "black"
antialiasing: true
}
@@ -56,6 +56,11 @@ Rectangle {
case "app":
if (selectedItem?.isCore)
break;
if (selectedItem?.actions) {
for (var i = 0; i < selectedItem.actions.length; i++) {
result.push(selectedItem.actions[i]);
}
}
if (SessionService.nvidiaCommand) {
result.push({
name: I18n.tr("Launch on dGPU"),
@@ -63,11 +68,6 @@ Rectangle {
action: "launch_dgpu"
});
}
if (selectedItem?.actions) {
for (var i = 0; i < selectedItem.actions.length; i++) {
result.push(selectedItem.actions[i]);
}
}
break;
}
return result;
+39 -56
View File
@@ -125,6 +125,13 @@ Item {
}
readonly property var sectionDefinitions: [
{
id: "calculator",
title: I18n.tr("Calculator"),
icon: "calculate",
priority: 0,
defaultViewMode: "list"
},
{
id: "favorites",
title: I18n.tr("Pinned"),
@@ -674,6 +681,12 @@ Item {
return;
}
var calculatorResult = evaluateCalculator(searchQuery);
if (calculatorResult) {
calculatorResult._preScored = 12000;
allItems.push(calculatorResult);
}
var apps = searchApps(searchQuery);
for (var i = 0; i < apps.length; i++) {
if (searchQuery)
@@ -684,15 +697,12 @@ Item {
if (searchMode === "all") {
if (searchQuery && searchQuery.length >= 2) {
_pluginPhasePending = true;
_phase1Items = allItems.slice();
_pluginPhaseForceFirst = shouldResetSelection;
_phase1Items = allItems;
pluginPhaseTimer.restart();
if (allItems.length === 0) {
_pluginPhaseForceFirst = shouldResetSelection;
isSearching = true;
searchCompleted();
return;
}
_pluginPhaseForceFirst = false;
isSearching = true;
searchCompleted();
return;
} else if (!searchQuery) {
var emptyTriggerOrdered = getEmptyTriggerPluginsOrdered();
for (var i = 0; i < emptyTriggerOrdered.length; i++) {
@@ -921,6 +931,13 @@ Item {
return Transform.transformFileResult(file, I18n.tr("Open"), I18n.tr("Open folder"), I18n.tr("Copy path"));
}
function evaluateCalculator(query) {
var calc = Utils.evaluateCalculator(query);
if (!calc)
return null;
return Transform.createCalculatorItem(calc, query, I18n.tr("Copy"));
}
function detectTrigger(query) {
if (!query || query.length === 0)
return {
@@ -1253,23 +1270,6 @@ Item {
CacheData.saveLauncherCache(serializable);
}
function _actionsFromDesktopEntry(appId) {
if (!appId)
return [];
var entry = DesktopEntries.heuristicLookup(appId);
if (!entry || !entry.actions || entry.actions.length === 0)
return [];
var result = [];
for (var i = 0; i < entry.actions.length; i++) {
result.push({
name: entry.actions[i].name,
icon: "play_arrow",
actionData: entry.actions[i]
});
}
return result;
}
function _loadDiskCache() {
var cached = CacheData.loadLauncherCache();
if (!cached || !Array.isArray(cached) || cached.length === 0)
@@ -1297,12 +1297,8 @@ Item {
data: {
id: it.id
},
actions: _actionsFromDesktopEntry(it.id),
primaryAction: it.type === "app" && !it.isCore ? {
name: I18n.tr("Launch"),
icon: "open_in_new",
action: "launch"
} : null,
actions: [],
primaryAction: null,
_diskCached: true,
_hName: "",
_hSub: "",
@@ -1563,6 +1559,9 @@ Item {
case "file":
openFile(item.data?.path);
break;
case "calculator":
copyToClipboard(item.name);
break;
default:
return;
}
@@ -1617,41 +1616,25 @@ Item {
itemExecuted();
}
function _resolveDesktopEntry(app) {
if (!app)
return null;
if (app.command)
return app;
var id = app.id || app.execString || app.exec || "";
if (!id)
return null;
return DesktopEntries.heuristicLookup(id);
}
function launchApp(app) {
var entry = _resolveDesktopEntry(app);
if (!entry)
if (!app)
return;
SessionService.launchDesktopEntry(entry);
AppUsageHistoryData.addAppUsage(entry);
SessionService.launchDesktopEntry(app);
AppUsageHistoryData.addAppUsage(app);
}
function launchAppWithNvidia(app) {
var entry = _resolveDesktopEntry(app);
if (!entry)
if (!app)
return;
SessionService.launchDesktopEntry(entry, true);
AppUsageHistoryData.addAppUsage(entry);
SessionService.launchDesktopEntry(app, true);
AppUsageHistoryData.addAppUsage(app);
}
function launchAppAction(actionItem) {
if (!actionItem || !actionItem.actionData)
if (!actionItem || !actionItem.parentApp || !actionItem.actionData)
return;
var entry = _resolveDesktopEntry(actionItem.parentApp);
if (!entry)
return;
SessionService.launchDesktopAction(entry, actionItem.actionData);
AppUsageHistoryData.addAppUsage(entry);
SessionService.launchDesktopAction(actionItem.parentApp, actionItem.actionData);
AppUsageHistoryData.addAppUsage(actionItem.parentApp);
}
function openFile(path) {
@@ -101,6 +101,35 @@ function detectIconType(iconName) {
return "material";
}
function evaluateCalculator(query) {
if (!query || query.length === 0)
return null;
var mathExpr = query.replace(/[^0-9+\-*/().%\s^]/g, "");
if (mathExpr.length < 2)
return null;
var hasMath = /[+\-*/^%]/.test(query) && /\d/.test(query);
if (!hasMath)
return null;
try {
var sanitized = mathExpr.replace(/\^/g, "**");
var result = Function('"use strict"; return (' + sanitized + ')')();
if (typeof result === "number" && isFinite(result)) {
var displayResult = Number.isInteger(result) ? result.toString() : result.toFixed(6).replace(/\.?0+$/, "");
return {
expression: query,
result: result,
displayResult: displayResult
};
}
} catch (e) { }
return null;
}
function sortPluginIdsByOrder(pluginIds, order) {
if (!order || order.length === 0)
return pluginIds;
@@ -190,6 +190,32 @@ function transformPluginItem(item, pluginId, selectLabel) {
};
}
function createCalculatorItem(calc, query, copyLabel) {
return {
id: "calculator_result",
type: "calculator",
name: calc.displayResult,
subtitle: query + " =",
icon: "calculate",
iconType: "material",
section: "calculator",
data: {
expression: calc.expression,
result: calc.result
},
actions: [],
primaryAction: {
name: copyLabel,
icon: "content_copy",
action: "copy"
},
_hName: "",
_hSub: "",
_hRich: false,
_preScored: undefined
};
}
function createPluginBrowseItem(pluginId, plugin, trigger, isBuiltIn, isAllowed, browseLabel, triggerLabel, noTriggerLabel) {
var rawIcon = isBuiltIn ? (plugin.cornerIcon || "extension") : (plugin.icon || "extension");
return {
@@ -470,6 +470,9 @@ FocusScope {
onTextChanged: {
controller.setSearchQuery(text);
if (text.length === 0) {
controller.restorePreviousMode();
}
if (actionPanel.expanded) {
actionPanel.hide();
}
@@ -178,6 +178,8 @@ Rectangle {
if (!root.item)
return "";
switch (root.item.type) {
case "calculator":
return I18n.tr("Calc");
case "plugin":
return I18n.tr("Plugin");
case "file":
@@ -238,37 +238,37 @@ FocusScope {
property var quickAccessLocations: [
{
"name": I18n.tr("Home"),
"name": "Home",
"path": homeDir,
"icon": "home"
},
{
"name": I18n.tr("Documents"),
"name": "Documents",
"path": docsDir,
"icon": "description"
},
{
"name": I18n.tr("Downloads"),
"name": "Downloads",
"path": downloadDir,
"icon": "download"
},
{
"name": I18n.tr("Pictures"),
"name": "Pictures",
"path": picsDir,
"icon": "image"
},
{
"name": I18n.tr("Music"),
"name": "Music",
"path": musicDir,
"icon": "music_note"
},
{
"name": I18n.tr("Videos"),
"name": "Videos",
"path": videosDir,
"icon": "movie"
},
{
"name": I18n.tr("Desktop"),
"name": "Desktop",
"path": desktopDir,
"icon": "computer"
}
@@ -19,7 +19,7 @@ StyledRect {
spacing: 4
StyledText {
text: I18n.tr("Quick Access")
text: "Quick Access"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
font.weight: Font.Medium
+2 -23
View File
@@ -91,12 +91,7 @@ DankModal {
id: searchField
Layout.alignment: Qt.AlignRight
leftIconName: "search"
keyForwardTargets: [root.modalFocusScope]
onTextEdited: searchDebounce.restart()
Keys.onEscapePressed: event => {
root.close();
event.accepted = true;
}
}
}
@@ -123,7 +118,6 @@ DankModal {
function generateCategories(query) {
const lowerQuery = query ? query.toLowerCase().trim() : "";
const lowerQueryWords = query.split(/\s+/);
const processed = {};
for (const cat in rawBinds) {
@@ -136,24 +130,9 @@ DankModal {
const keyLower = bind.key.toLowerCase();
const descLower = bind.desc.toLowerCase();
const actionLower = bind.action.toLowerCase();
if (bind.hideOnOverlay)
if (!(lowerQuery.length === 0 || keyLower.includes(lowerQuery) || descLower.includes(lowerQuery) || catLower.includes(lowerQuery) || actionLower.includes(lowerQuery)))
continue;
let shouldContinue = false;
for (let j = 0; j < lowerQueryWords.length; j++) {
const word = lowerQueryWords[j];
if (!(
word.length === 0 ||
keyLower.includes(word) ||
descLower.includes(word) ||
catLower.includes(word) ||
actionLower.includes(word)
)) {
shouldContinue = true;
break;
}
}
if (shouldContinue)
if (bind.hideOnOverlay)
continue;
if (bind.subcat) {
+14 -17
View File
@@ -83,9 +83,9 @@ FloatingWindow {
objectName: "processListModal"
title: I18n.tr("System Monitor", "sysmon window title")
minimumSize: Qt.size(Math.min(Math.round(Theme.fontSizeMedium * 48), Screen.width), Math.min(Math.round(Theme.fontSizeMedium * 34), Screen.height))
implicitWidth: Math.round(Theme.fontSizeMedium * 71)
implicitHeight: Math.round(Theme.fontSizeMedium * 51)
minimumSize: Qt.size(750, 550)
implicitWidth: 1000
implicitHeight: 720
color: Theme.surfaceContainer
visible: false
@@ -236,7 +236,7 @@ FloatingWindow {
Item {
Layout.fillWidth: true
Layout.preferredHeight: Math.round(Theme.fontSizeMedium * 3.4)
Layout.preferredHeight: 48
MouseArea {
anchors.fill: parent
@@ -293,10 +293,10 @@ FloatingWindow {
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: Math.round(Theme.fontSizeMedium * 3.7)
Layout.preferredHeight: 52
Layout.leftMargin: Theme.spacingL
Layout.rightMargin: Theme.spacingL
spacing: Theme.spacingM
spacing: Theme.spacingL
Row {
spacing: 2
@@ -322,15 +322,14 @@ FloatingWindow {
]
Rectangle {
width: tabRowContent.implicitWidth + Theme.spacingM * 2
height: Math.round(Theme.fontSizeMedium * 3.1)
width: 120
height: 44
radius: Theme.cornerRadius
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
border.color: currentTab === index ? Theme.primary : "transparent"
border.width: currentTab === index ? 1 : 0
Row {
id: tabRowContent
anchors.centerIn: parent
spacing: Theme.spacingXS
@@ -374,13 +373,11 @@ FloatingWindow {
DankButtonGroup {
id: processFilterGroup
Layout.minimumWidth: implicitWidth + 8
model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")]
currentIndex: 0
checkEnabled: false
buttonHeight: Math.round(Theme.fontSizeSmall * 2.6)
minButtonWidth: 0
buttonPadding: Theme.spacingS
textSize: Theme.fontSizeSmall
buttonHeight: 36
visible: currentTab === 0
onSelectionChanged: (index, selected) => {
if (!selected)
@@ -403,9 +400,9 @@ FloatingWindow {
DankTextField {
id: searchField
Layout.fillWidth: true
Layout.maximumWidth: Math.round(Theme.fontSizeMedium * 18)
Layout.minimumWidth: Theme.fontSizeMedium * 4
Layout.preferredHeight: Math.round(Theme.fontSizeMedium * 2.8)
Layout.maximumWidth: 250
Layout.minimumWidth: 120
Layout.preferredHeight: 40
placeholderText: I18n.tr("Search processes...", "process search placeholder")
leftIconName: "search"
showClearButton: true
@@ -473,7 +470,7 @@ FloatingWindow {
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: Math.round(Theme.fontSizeSmall * 2.7)
Layout.preferredHeight: 32
Layout.leftMargin: Theme.spacingL
Layout.rightMargin: Theme.spacingL
Layout.bottomMargin: Theme.spacingM
@@ -8,39 +8,10 @@ DankPopout {
layerNamespace: "dms:app-launcher"
property string _pendingMode: ""
property string _pendingQuery: ""
function show() {
open();
}
function openWithMode(mode) {
_pendingMode = mode || "";
open();
}
function toggleWithMode(mode) {
if (shouldBeVisible) {
close();
return;
}
openWithMode(mode);
}
function openWithQuery(query) {
_pendingQuery = query || "";
open();
}
function toggleWithQuery(query) {
if (shouldBeVisible) {
close();
return;
}
openWithQuery(query);
}
popupWidth: 560
popupHeight: 640
triggerWidth: 40
@@ -59,25 +30,15 @@ DankPopout {
var lc = contentLoader.item?.launcherContent;
if (!lc)
return;
const query = _pendingQuery;
const mode = _pendingMode || "apps";
_pendingMode = "";
_pendingQuery = "";
if (lc.searchField) {
lc.searchField.text = query;
lc.searchField.text = "";
lc.searchField.forceActiveFocus();
}
if (lc.controller) {
lc.controller.searchMode = mode;
lc.controller.searchMode = "apps";
lc.controller.pluginFilter = "";
lc.controller.searchQuery = "";
if (query) {
lc.controller.setSearchQuery(query);
} else {
lc.controller.performSearch();
}
lc.controller.performSearch();
}
lc.resetScroll?.();
lc.actionPanel?.hide();
@@ -15,22 +15,18 @@ Item {
property var pluginDetailInstance: null
property var widgetModel: null
property var collapseCallback: null
property real maxAvailableHeight: 9999
function getDetailHeight(section) {
const maxAvailable = parent ? parent.height - Theme.spacingS : 9999;
switch (true) {
case section === "wifi":
case section === "bluetooth":
case section === "builtin_vpn":
return Math.min(350, maxAvailableHeight);
return Math.min(350, maxAvailable);
case section.startsWith("brightnessSlider_"):
return Math.min(400, maxAvailableHeight);
case section.startsWith("plugin_"):
if (pluginDetailInstance?.ccDetailHeight)
return Math.min(pluginDetailInstance.ccDetailHeight, maxAvailableHeight);
return Math.min(250, maxAvailableHeight);
return Math.min(400, maxAvailable);
default:
return Math.min(250, maxAvailableHeight);
return Math.min(250, maxAvailable);
}
}
@@ -31,26 +31,11 @@ Column {
spacing: editMode ? Theme.spacingL : Theme.spacingS
property real maxPopoutHeight: 9999
property var currentRowWidgets: []
property real currentRowWidth: 0
property int expandedRowIndex: -1
property var colorPickerModal: null
readonly property real _maxDetailHeight: {
const rows = layoutResult.rows;
let totalRowHeight = 0;
for (let i = 0; i < rows.length; i++) {
const sliderOnly = rows[i].every(w => {
const id = w.id || "";
return id === "volumeSlider" || id === "brightnessSlider" || id === "inputVolumeSlider";
});
totalRowHeight += sliderOnly ? 36 : 60;
}
const rowSpacing = Math.max(0, rows.length - 1) * spacing;
return Math.max(100, maxPopoutHeight - totalRowHeight - rowSpacing);
}
function calculateRowsAndWidgets() {
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex);
}
@@ -178,7 +163,6 @@ Column {
DetailHost {
id: detailHost
width: parent.width
maxAvailableHeight: root._maxDetailHeight
height: active ? (getDetailHeight(root.expandedSection) + Theme.spacingS) : 0
property bool active: {
if (root.expandedSection === "")
@@ -116,7 +116,6 @@ DankPopout {
property alias bluetoothCodecSelector: bluetoothCodecSelector
color: "transparent"
clip: true
Rectangle {
anchors.fill: parent
@@ -167,10 +166,6 @@ DankPopout {
id: widgetGrid
width: parent.width
editMode: root.editMode
maxPopoutHeight: {
const screenHeight = (root.triggerScreen?.height ?? 1080);
return screenHeight - 100 - Theme.spacingL - headerPane.height - Theme.spacingS;
}
expandedSection: root.expandedSection
expandedWidgetIndex: root.expandedWidgetIndex
expandedWidgetData: root.expandedWidgetData
@@ -40,24 +40,6 @@ Rectangle {
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
Item {
height: 1
width: parent.width - headerText.width - settingsButton.width
}
DankActionButton {
id: settingsButton
anchors.verticalCenter: parent.verticalCenter
iconName: "settings"
buttonSize: 28
iconSize: 16
iconColor: Theme.surfaceVariantText
onClicked: {
PopoutService.closeControlCenter();
PopoutService.openSettingsWithTab("audio");
}
}
}
Row {
@@ -169,11 +151,8 @@ Rectangle {
Repeater {
model: ScriptModel {
values: {
const hidden = SessionData.hiddenInputDeviceNames ?? [];
const nodes = Pipewire.nodes.values.filter(node => {
if (!node.audio || node.isSink || node.isStream)
return false;
return !hidden.includes(node.name);
return node.audio && !node.isSink && !node.isStream;
});
const pinnedList = audioContent.getPinnedInputs();
@@ -40,24 +40,6 @@ Rectangle {
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
Item {
height: 1
width: parent.width - headerText.width - settingsButton.width
}
DankActionButton {
id: settingsButton
anchors.verticalCenter: parent.verticalCenter
iconName: "settings"
buttonSize: 28
iconSize: 16
iconColor: Theme.surfaceVariantText
onClicked: {
PopoutService.closeControlCenter();
PopoutService.openSettingsWithTab("audio");
}
}
}
Row {
@@ -179,11 +161,8 @@ Rectangle {
Repeater {
model: ScriptModel {
values: {
const hidden = SessionData.hiddenOutputDeviceNames ?? [];
const nodes = Pipewire.nodes.values.filter(node => {
if (!node.audio || !node.isSink || node.isStream)
return false;
return !hidden.includes(node.name);
return node.audio && node.isSink && !node.isStream;
});
const pinnedList = audioContent.getPinnedOutputs();
+12 -40
View File
@@ -642,52 +642,24 @@ Item {
popoutTarget: appDrawerLoader.item
parentScreen: barWindow.screen
hyprlandOverviewLoader: barWindow ? barWindow.hyprlandOverviewLoader : null
function _preparePopout() {
onClicked: {
appDrawerLoader.active = true;
if (!appDrawerLoader.item)
return false;
// Use topBarContent.barConfig directly since widget barConfig binding doesn't work in Components
const effectiveBarConfig = topBarContent.barConfig;
// Calculate barPosition from axis.edge
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
if (appDrawerLoader.item.setBarContext)
if (appDrawerLoader.item && appDrawerLoader.item.setBarContext) {
appDrawerLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
if (appDrawerLoader.item.setTriggerPosition) {
}
if (appDrawerLoader.item && appDrawerLoader.item.setTriggerPosition) {
const globalPos = launcherButton.visualContent.mapToItem(null, 0, 0);
const currentScreen = barWindow.screen;
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barWindow.effectiveBarThickness, launcherButton.visualWidth, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
appDrawerLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, launcherButton.section, currentScreen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
}
return true;
}
function openWithMode(mode) {
if (!_preparePopout())
return;
appDrawerLoader.item.openWithMode(mode);
}
function toggleWithMode(mode) {
if (!_preparePopout())
return;
appDrawerLoader.item.toggleWithMode(mode);
}
function openWithQuery(query) {
if (!_preparePopout())
return;
appDrawerLoader.item.openWithQuery(query);
}
function toggleWithQuery(query) {
if (!_preparePopout())
return;
appDrawerLoader.item.toggleWithQuery(query);
}
onClicked: {
if (!_preparePopout())
return;
PopoutManager.requestPopout(appDrawerLoader.item, undefined, "appDrawer");
if (appDrawerLoader.item) {
PopoutManager.requestPopout(appDrawerLoader.item, undefined, "appDrawer");
}
}
}
}
@@ -811,7 +783,7 @@ Item {
} else {
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
}
PopoutManager.requestPopout(dankDashPopoutLoader.item, 0, (effectiveBarConfig?.id ?? "default") + "-" + section + "-0");
PopoutManager.requestPopout(dankDashPopoutLoader.item, 0, (effectiveBarConfig?.id ?? "default") + "-0");
}
}
}
@@ -867,7 +839,7 @@ Item {
} else {
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
}
PopoutManager.requestPopout(dankDashPopoutLoader.item, 1, (effectiveBarConfig?.id ?? "default") + "-" + section + "-1");
PopoutManager.requestPopout(dankDashPopoutLoader.item, 1, (effectiveBarConfig?.id ?? "default") + "-1");
}
}
}
@@ -926,7 +898,7 @@ Item {
} else {
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
}
PopoutManager.requestPopout(dankDashPopoutLoader.item, 3, (effectiveBarConfig?.id ?? "default") + "-" + section + "-3");
PopoutManager.requestPopout(dankDashPopoutLoader.item, 3, (effectiveBarConfig?.id ?? "default") + "-3");
}
}
}
+1 -1
View File
@@ -80,7 +80,7 @@ PanelWindow {
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
}
PopoutManager.requestPopout(dankDashPopoutLoader.item, 2, (barConfig?.id ?? "default") + "-" + section + "-2");
PopoutManager.requestPopout(dankDashPopoutLoader.item, 2, (barConfig?.id ?? "default") + "-2");
}
readonly property var dBarLayer: {
@@ -99,6 +99,13 @@ BasePill {
width: implicitWidth
height: implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
StyledTextMetrics {
id: cpuBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
@@ -99,6 +99,13 @@ BasePill {
width: implicitWidth
height: implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
StyledTextMetrics {
id: tempBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
@@ -193,6 +193,13 @@ BasePill {
}
width: Math.max(diskBaseline.width, paintedWidth)
Behavior on width {
NumberAnimation {
duration: 120
easing.type: Easing.OutCubic
}
}
}
}
}
@@ -167,6 +167,13 @@ BasePill {
width: implicitWidth
height: implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
StyledTextMetrics {
id: gpuTempBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
@@ -110,6 +110,13 @@ BasePill {
}
width: Math.max(rxBaseline.width, paintedWidth)
Behavior on width {
NumberAnimation {
duration: 120
easing.type: Easing.OutCubic
}
}
}
}
@@ -139,6 +146,13 @@ BasePill {
}
width: Math.max(txBaseline.width, paintedWidth)
Behavior on width {
NumberAnimation {
duration: 120
easing.type: Easing.OutCubic
}
}
}
}
}
@@ -109,6 +109,13 @@ BasePill {
width: implicitWidth
height: implicitHeight
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
StyledTextMetrics {
id: ramBaseline
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
@@ -138,13 +138,15 @@ BasePill {
readonly property real iconCellSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) + 6
readonly property string focusedAppId: {
if (!sortedToplevels || sortedToplevels.length === 0)
const toplevels = CompositorService.sortedToplevels;
if (!toplevels)
return "";
for (let i = 0; i < sortedToplevels.length; i++) {
if (sortedToplevels[i].activated)
return sortedToplevels[i].appId || "";
let result = "";
for (let i = 0; i < toplevels.length; i++) {
if (toplevels[i].activated)
result = toplevels[i].appId || "";
}
return "";
return result;
}
visible: windowCount > 0
@@ -153,7 +155,6 @@ BasePill {
property real touchpadThreshold: 500
onWheel: function (wheelEvent) {
wheelEvent.accepted = true;
const deltaY = wheelEvent.angleDelta.y;
const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;
@@ -99,44 +99,6 @@ BasePill {
property bool suppressShiftAnimation: false
readonly property bool hasHiddenItems: allTrayItems.length > mainBarItems.length
visible: allTrayItems.length > 0
opacity: allTrayItems.length > 0 ? 1 : 0
states: [
State {
name: "hidden_horizontal"
when: allTrayItems.length === 0 && !isVerticalOrientation
PropertyChanges {
target: root
width: 0
}
},
State {
name: "hidden_vertical"
when: allTrayItems.length === 0 && isVerticalOrientation
PropertyChanges {
target: root
height: 0
}
}
]
transitions: [
Transition {
NumberAnimation {
properties: "width,height"
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
]
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
readonly property real trayItemSize: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground) + 6
readonly property real minTooltipY: {
@@ -41,7 +41,7 @@ Card {
anchors.verticalCenter: parent.verticalCenter
StyledText {
text: UserInfoService.username || I18n.tr("brandon")
text: UserInfoService.username || "brandon"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
@@ -64,18 +64,18 @@ Card {
StyledText {
text: {
if (CompositorService.isNiri)
return I18n.tr("on Niri");
return "on niri";
if (CompositorService.isHyprland)
return I18n.tr("on Hyprland");
return "on Hyprland";
// technically they might not be on mangowc, but its what we support in the docs
if (CompositorService.isDwl)
return I18n.tr("on MangoWC");
return "on MangoWC";
if (CompositorService.isSway)
return I18n.tr("on Sway");
return "on Sway";
if (CompositorService.isScroll)
return I18n.tr("on Scroll");
return "on Scroll";
if (CompositorService.isMiracle)
return I18n.tr("on Miracle WM");
return "on Miracle WM";
return "";
}
font.pixelSize: Theme.fontSizeSmall
@@ -99,7 +99,7 @@ Card {
}
StyledText {
text: DgopService.shortUptime || I18n.tr("up")
text: DgopService.shortUptime || "up"
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
anchors.verticalCenter: parent.verticalCenter
+9 -1
View File
@@ -553,7 +553,15 @@ Variants {
Rectangle {
anchors.fill: parent
color: Theme.withAlpha(Theme.surfaceContainer, backgroundTransparency)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, backgroundTransparency)
border.color: Theme.outlineMedium
border.width: 1
radius: Theme.cornerRadius
}
Rectangle {
anchors.fill: parent
color: Qt.rgba(Theme.surfaceTint.r, Theme.surfaceTint.g, Theme.surfaceTint.b, 0.04)
radius: Theme.cornerRadius
}
}
+26 -60
View File
@@ -165,7 +165,7 @@ PanelWindow {
}
width: Math.min(400, Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2))
height: menuColumn.implicitHeight + Theme.spacingS * 2
height: Math.max(60, menuColumn.implicitHeight + Theme.spacingS * 2)
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
@@ -388,31 +388,18 @@ PanelWindow {
radius: Theme.cornerRadius
color: pinArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
StyledText {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
DankIcon {
anchors.verticalCenter: parent.verticalCenter
name: root.appData && root.appData.isPinned ? "keep_off" : "push_pin"
size: 14
color: Theme.surfaceText
opacity: 0.7
}
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: root.appData && root.appData.isPinned ? I18n.tr("Unpin from Dock") : I18n.tr("Pin to Dock")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
text: root.appData && root.appData.isPinned ? I18n.tr("Unpin from Dock") : I18n.tr("Pin to Dock")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
DankRipple {
@@ -461,31 +448,18 @@ PanelWindow {
radius: Theme.cornerRadius
color: nvidiaArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
StyledText {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
DankIcon {
anchors.verticalCenter: parent.verticalCenter
name: "memory"
size: 14
color: Theme.surfaceText
opacity: 0.7
}
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: I18n.tr("Launch on dGPU")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
text: I18n.tr("Launch on dGPU")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Normal
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
DankRipple {
@@ -516,31 +490,23 @@ PanelWindow {
radius: Theme.cornerRadius
color: closeArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
Row {
StyledText {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
DankIcon {
anchors.verticalCenter: parent.verticalCenter
name: "close"
size: 14
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
opacity: 0.7
}
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: root.appData && root.appData.type === "grouped" ? I18n.tr("Close All Windows") : I18n.tr("Close Window")
font.pixelSize: Theme.fontSizeSmall
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
font.weight: Font.Normal
elide: Text.ElideRight
wrapMode: Text.NoWrap
text: {
if (root.appData && root.appData.type === "grouped") {
return "Close All Windows";
}
return "Close Window";
}
font.pixelSize: Theme.fontSizeSmall
color: closeArea.containsMouse ? Theme.error : Theme.surfaceText
font.weight: Font.Normal
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
DankRipple {
@@ -1065,7 +1065,7 @@ Item {
}
StyledText {
text: I18n.tr("Caps Lock is on")
text: "Caps Lock is on"
font.pixelSize: Theme.fontSizeSmall
color: Theme.error
anchors.verticalCenter: parent.verticalCenter
@@ -278,7 +278,7 @@ Item {
Behavior on x {
enabled: !swipeDragHandler.active && delegateRoot.__delegateInitialized
NumberAnimation {
duration: Theme.notificationExitDuration
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
@@ -286,7 +286,7 @@ Item {
Behavior on opacity {
enabled: delegateRoot.__delegateInitialized
NumberAnimation {
duration: delegateRoot.__delegateInitialized ? Theme.notificationExitDuration : 0
duration: delegateRoot.__delegateInitialized ? Theme.shortDuration : 0
}
}
}
@@ -257,7 +257,7 @@ DankListView {
target: delegateRoot
property: "swipeOffset"
to: 0
duration: Theme.notificationExitDuration
duration: Theme.shortDuration
easing.type: Easing.OutCubic
onStopped: NotificationService.dismissGroup(delegateRoot.modelData?.key || "")
}
@@ -764,7 +764,7 @@ Rectangle {
target: expandedDelegateWrapper
property: "swipeOffset"
to: expandedDelegateWrapper.swipeOffset > 0 ? expandedDelegateWrapper.width : -expandedDelegateWrapper.width
duration: Theme.notificationExitDuration
duration: Theme.shortDuration
easing.type: Easing.OutCubic
onStopped: NotificationService.dismissNotification(modelData)
}
@@ -35,9 +35,9 @@ DankPopout {
popupWidth: triggerScreen ? Math.min(500, Math.max(380, triggerScreen.width - 48)) : 400
popupHeight: stablePopupHeight
positioning: ""
animationScaleCollapsed: 0.94
animationScaleCollapsed: 1.0
animationOffset: 0
suspendShadowWhileResizing: false
suspendShadowWhileResizing: true
screen: triggerScreen
shouldBeVisible: notificationHistoryVisible
@@ -23,7 +23,7 @@ Rectangle {
spacing: 2
StyledText {
text: I18n.tr("↑/↓: Nav • Space: Expand • Enter: Action/Expand • E: Text")
text: "↑/↓: Nav • Space: Expand • Enter: Action/Expand • E: Text"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
width: parent.width
@@ -22,8 +22,6 @@ PanelWindow {
property bool _isDestroying: false
property bool _finalized: false
property real _lastReportedAlignedHeight: -1
property real _storedTopMargin: 0
property real _storedBottomMargin: 0
readonly property string clearText: I18n.tr("Dismiss")
property bool descriptionExpanded: false
readonly property bool hasExpandableBody: (notificationData?.htmlBody || "").replace(/<[^>]*>/g, "").trim().length > 0
@@ -148,8 +146,6 @@ PanelWindow {
}
Component.onCompleted: {
_lastReportedAlignedHeight = Theme.px(implicitHeight, dpr);
_storedTopMargin = getTopMargin();
_storedBottomMargin = getBottomMargin();
if (SettingsData.notificationPopupPrivacyMode)
descriptionExpanded = false;
if (hasValidData) {
@@ -183,30 +179,14 @@ PanelWindow {
property bool isBottomCenter: SettingsData.notificationPopupPosition === SettingsData.Position.BottomCenter
property bool isCenterPosition: isTopCenter || isBottomCenter
anchors.top: true
anchors.bottom: true
anchors.top: isTopCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Left
anchors.bottom: isBottomCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom || SettingsData.notificationPopupPosition === SettingsData.Position.Right
anchors.left: SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
anchors.right: SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Right
mask: contentInputMask
Region {
id: contentInputMask
item: contentMaskRect
}
Item {
id: contentMaskRect
visible: false
x: content.x
y: content.y
width: alignedWidth
height: alignedHeight
}
margins {
top: _storedTopMargin
bottom: _storedBottomMargin
top: getTopMargin()
bottom: getBottomMargin()
left: getLeftMargin()
right: getRightMargin()
}
@@ -283,14 +263,7 @@ PanelWindow {
id: content
x: Theme.snap((win.width - alignedWidth) / 2, dpr)
y: {
const isTop = isTopCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Left;
if (isTop) {
return Theme.snap(screenY, dpr);
} else {
return Theme.snap(win.height - alignedHeight - screenY, dpr);
}
}
y: Theme.snap((win.height - alignedHeight) / 2, dpr)
width: alignedWidth
height: alignedHeight
visible: !win._finalized
@@ -338,7 +311,7 @@ PanelWindow {
id: bgShadowLayer
anchors.fill: parent
anchors.margins: Theme.snap(4, win.dpr)
layer.enabled: !win._isDestroying && win.screenValid
layer.enabled: !win._isDestroying && win.screenValid && !implicitHeightAnim.running
layer.smooth: false
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
layer.textureMirroring: ShaderEffectSource.MirrorVertically
@@ -841,7 +814,7 @@ PanelWindow {
Behavior on swipeOffset {
enabled: !content.swipeActive && !content.swipeDismissing
NumberAnimation {
duration: Theme.notificationExitDuration
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
@@ -851,7 +824,7 @@ PanelWindow {
target: content
property: "swipeOffset"
to: isTopCenter ? -content.height : isBottomCenter ? content.height : (SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom ? -content.width : content.width)
duration: Theme.notificationExitDuration
duration: Theme.shortDuration
easing.type: Easing.OutCubic
onStopped: {
NotificationService.dismissNotification(notificationData);
+53 -32
View File
@@ -7,10 +7,9 @@ DankOSD {
id: root
readonly property bool useVertical: isVerticalLayout
property int _displayBrightness: 0
function _syncBrightness() {
_displayBrightness = DisplayService.brightnessLevel;
property int targetBrightness: {
DisplayService.brightnessVersion;
return DisplayService.brightnessLevel;
}
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
@@ -21,9 +20,9 @@ DankOSD {
Connections {
target: DisplayService
function onBrightnessChanged(showOsd) {
root._syncBrightness();
if (showOsd && SettingsData.osdBrightnessEnabled)
if (showOsd && SettingsData.osdBrightnessEnabled) {
root.show();
}
}
}
@@ -54,11 +53,13 @@ DankOSD {
anchors.centerIn: parent
name: {
const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc")
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc") {
return "brightness_medium";
if (deviceInfo.name.includes("kbd"))
} else if (deviceInfo.name.includes("kbd")) {
return "keyboard";
return "lightbulb";
} else {
return "lightbulb";
}
}
size: Theme.iconSize
color: Theme.primary
@@ -76,16 +77,20 @@ DankOSD {
const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo)
return 1;
if (SessionData.getBrightnessExponential(deviceInfo.id))
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
if (isExponential) {
return 1;
}
return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0;
}
maximum: {
const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo)
return 100;
if (SessionData.getBrightnessExponential(deviceInfo.id))
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
if (isExponential) {
return 100;
}
return deviceInfo.displayMax || 100;
}
enabled: DisplayService.brightnessAvailable
@@ -94,24 +99,28 @@ DankOSD {
const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo)
return "%";
if (SessionData.getBrightnessExponential(deviceInfo.id))
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
if (isExponential) {
return "%";
}
return deviceInfo.class === "ddc" ? "" : "%";
}
thumbOutlineColor: Theme.surfaceContainer
alwaysShowValue: SettingsData.osdAlwaysShowValue
onSliderValueChanged: newValue => {
if (!DisplayService.brightnessAvailable)
return;
DisplayService.setBrightness(newValue, DisplayService.lastIpcDevice, true);
resetHideTimer();
if (DisplayService.brightnessAvailable) {
DisplayService.setBrightness(newValue, DisplayService.lastIpcDevice, true);
resetHideTimer();
}
}
onContainsMouseChanged: setChildHovered(containsMouse)
onContainsMouseChanged: {
setChildHovered(containsMouse);
}
Binding on value {
value: root._displayBrightness
value: root.targetBrightness
when: !brightnessSlider.isDragging
}
}
@@ -137,11 +146,13 @@ DankOSD {
anchors.centerIn: parent
name: {
const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc")
if (!deviceInfo || deviceInfo.class === "backlight" || deviceInfo.class === "ddc") {
return "brightness_medium";
if (deviceInfo.name.includes("kbd"))
} else if (deviceInfo.name.includes("kbd")) {
return "keyboard";
return "lightbulb";
} else {
return "lightbulb";
}
}
size: Theme.iconSize
color: Theme.primary
@@ -159,7 +170,7 @@ DankOSD {
property int value: 50
Binding on value {
value: root._displayBrightness
value: root.targetBrightness
when: !vertSlider.dragging
}
@@ -167,7 +178,8 @@ DankOSD {
const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo)
return 1;
if (SessionData.getBrightnessExponential(deviceInfo.id))
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
if (isExponential)
return 1;
return (deviceInfo.class === "backlight" || deviceInfo.class === "ddc") ? 1 : 0;
}
@@ -176,7 +188,8 @@ DankOSD {
const deviceInfo = DisplayService.getCurrentDeviceInfo();
if (!deviceInfo)
return 100;
if (SessionData.getBrightnessExponential(deviceInfo.id))
const isExponential = SessionData.getBrightnessExponential(deviceInfo.id);
if (isExponential)
return 100;
return deviceInfo.displayMax || 100;
}
@@ -227,25 +240,33 @@ DankOSD {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: setChildHovered(containsMouse)
onContainsMouseChanged: {
setChildHovered(containsMouse);
}
onPressed: mouse => {
vertSlider.dragging = true;
updateBrightness(mouse);
}
onReleased: vertSlider.dragging = false
onPositionChanged: mouse => {
if (pressed)
updateBrightness(mouse);
onReleased: {
vertSlider.dragging = false;
}
onClicked: mouse => updateBrightness(mouse)
onPositionChanged: mouse => {
if (pressed) {
updateBrightness(mouse);
}
}
onClicked: mouse => {
updateBrightness(mouse);
}
function updateBrightness(mouse) {
if (!DisplayService.brightnessAvailable)
if (!DisplayService.brightnessAvailable) {
return;
}
const ratio = 1.0 - (mouse.y / height);
const newValue = Math.round(vertSlider.minimum + ratio * (vertSlider.maximum - vertSlider.minimum));
vertSlider.value = newValue;
+68 -35
View File
@@ -8,20 +8,13 @@ DankOSD {
readonly property bool useVertical: isVerticalLayout
readonly property var player: MprisController.activePlayer
readonly property int currentVolume: player ? Math.min(100, Math.round(player.volume * 100)) : 0
readonly property bool volumeSupported: player?.volumeSupported ?? false
property bool _suppressNewPlayer: false
property int _displayVolume: 0
function _syncVolume() {
if (!player)
return;
_displayVolume = Math.min(100, Math.round(player.volume * 100));
}
onPlayerChanged: {
_suppressNewPlayer = true;
_suppressTimer.restart();
_syncVolume();
}
Timer {
@@ -44,25 +37,25 @@ DankOSD {
}
function toggleMute() {
if (!player)
return;
player.volume = player.volume > 0 ? 0 : 1;
if (player) {
player.volume = player.volume > 0 ? 0 : 1;
}
}
function setVolume(volumePercent) {
if (!player)
return;
player.volume = volumePercent / 100;
resetHideTimer();
if (player) {
player.volume = volumePercent / 100;
resetHideTimer();
}
}
Connections {
target: player
function onVolumeChanged() {
root._syncVolume();
if (SettingsData.osdMediaVolumeEnabled && volumeSupported && !_suppressNewPlayer)
if (SettingsData.osdMediaVolumeEnabled && volumeSupported && !_suppressNewPlayer) {
root.show();
}
}
}
@@ -103,7 +96,9 @@ DankOSD {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: toggleMute()
onContainsMouseChanged: setChildHovered(containsMouse || volumeSlider.containsMouse)
onContainsMouseChanged: {
setChildHovered(containsMouse || volumeSlider.containsMouse);
}
}
}
@@ -120,21 +115,29 @@ DankOSD {
showValue: true
unit: "%"
thumbOutlineColor: Theme.surfaceContainer
valueOverride: root._displayVolume
valueOverride: currentVolume
alwaysShowValue: SettingsData.osdAlwaysShowValue
Component.onCompleted: {
root._syncVolume();
value = root._displayVolume;
value = currentVolume;
}
onSliderValueChanged: newValue => setVolume(newValue)
onSliderValueChanged: newValue => {
setVolume(newValue);
}
onContainsMouseChanged: setChildHovered(containsMouse || muteButton.containsMouse)
onContainsMouseChanged: {
setChildHovered(containsMouse || muteButton.containsMouse);
}
Binding on value {
value: root._displayVolume
when: !volumeSlider.pressed
Connections {
target: player
function onVolumeChanged() {
if (volumeSlider && !volumeSlider.pressed) {
volumeSlider.value = currentVolume;
}
}
}
}
}
@@ -169,7 +172,9 @@ DankOSD {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: toggleMute()
onContainsMouseChanged: setChildHovered(containsMouse || vertSliderArea.containsMouse)
onContainsMouseChanged: {
setChildHovered(containsMouse || vertSliderArea.containsMouse);
}
}
}
@@ -181,7 +186,7 @@ DankOSD {
y: gap * 2 + Theme.iconSize
property bool dragging: false
property int value: root._displayVolume
property int value: currentVolume
Rectangle {
id: vertTrack
@@ -226,21 +231,28 @@ DankOSD {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: setChildHovered(containsMouse || muteButtonVert.containsMouse)
onContainsMouseChanged: {
setChildHovered(containsMouse || muteButtonVert.containsMouse);
}
onPressed: mouse => {
vertSlider.dragging = true;
updateVolume(mouse);
}
onReleased: vertSlider.dragging = false
onPositionChanged: mouse => {
if (pressed)
updateVolume(mouse);
onReleased: {
vertSlider.dragging = false;
}
onClicked: mouse => updateVolume(mouse)
onPositionChanged: mouse => {
if (pressed) {
updateVolume(mouse);
}
}
onClicked: mouse => {
updateVolume(mouse);
}
function updateVolume(mouse) {
const ratio = 1.0 - (mouse.y / height);
@@ -248,6 +260,16 @@ DankOSD {
setVolume(volume);
}
}
Connections {
target: player
function onVolumeChanged() {
if (!vertSlider.dragging) {
vertSlider.value = currentVolume;
}
}
}
}
StyledText {
@@ -261,4 +283,15 @@ DankOSD {
}
}
}
onOsdShown: {
if (player && contentLoader.item && contentLoader.item.item) {
if (!useVertical) {
const slider = contentLoader.item.item.children[0].children[1];
if (slider && slider.value !== undefined) {
slider.value = currentVolume;
}
}
}
}
}
+88 -49
View File
@@ -7,13 +7,6 @@ DankOSD {
id: root
readonly property bool useVertical: isVerticalLayout
property int _displayVolume: 0
function _syncVolume() {
if (!AudioService.sink?.audio)
return;
_displayVolume = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
}
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
osdHeight: useVertical ? Math.min(260, Screen.height - Theme.spacingM * 2) : (40 + Theme.spacingS * 2)
@@ -21,17 +14,18 @@ DankOSD {
enableMouseInteraction: true
Connections {
target: AudioService.sink?.audio ?? null
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
function onVolumeChanged() {
root._syncVolume();
if (SettingsData.osdVolumeEnabled)
if (SettingsData.osdVolumeEnabled) {
root.show();
}
}
function onMutedChanged() {
if (SettingsData.osdVolumeEnabled)
if (SettingsData.osdVolumeEnabled) {
root.show();
}
}
}
@@ -39,9 +33,9 @@ DankOSD {
target: AudioService
function onSinkChanged() {
root._syncVolume();
if (root.shouldBeVisible && SettingsData.osdVolumeEnabled)
if (root.shouldBeVisible && SettingsData.osdVolumeEnabled) {
root.show();
}
}
}
@@ -70,7 +64,7 @@ DankOSD {
DankIcon {
anchors.centerIn: parent
name: AudioService.sink?.audio?.muted ? "volume_off" : "volume_up"
name: AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.muted ? "volume_off" : "volume_up"
size: Theme.iconSize
color: muteButton.containsMouse ? Theme.primary : Theme.surfaceText
}
@@ -81,45 +75,60 @@ DankOSD {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: AudioService.toggleMute()
onContainsMouseChanged: setChildHovered(containsMouse || volumeSlider.containsMouse)
onClicked: {
AudioService.toggleMute();
}
onContainsMouseChanged: {
setChildHovered(containsMouse || volumeSlider.containsMouse);
}
}
}
DankSlider {
id: volumeSlider
readonly property real actualVolumePercent: AudioService.sink && AudioService.sink.audio ? Math.round(AudioService.sink.audio.volume * 100) : 0
readonly property real displayPercent: actualVolumePercent
width: parent.width - Theme.iconSize - parent.gap * 3
height: 40
x: parent.gap * 2 + Theme.iconSize
anchors.verticalCenter: parent.verticalCenter
minimum: 0
maximum: AudioService.sinkMaxVolume
enabled: AudioService.sink?.audio
enabled: AudioService.sink && AudioService.sink.audio
showValue: true
unit: "%"
thumbOutlineColor: Theme.surfaceContainer
valueOverride: root._displayVolume
valueOverride: displayPercent
alwaysShowValue: SettingsData.osdAlwaysShowValue
Component.onCompleted: {
root._syncVolume();
value = root._displayVolume;
if (AudioService.sink && AudioService.sink.audio) {
value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
}
}
onSliderValueChanged: newValue => {
if (!AudioService.sink?.audio)
return;
SessionData.suppressOSDTemporarily();
AudioService.sink.audio.volume = newValue / 100;
resetHideTimer();
if (AudioService.sink && AudioService.sink.audio) {
SessionData.suppressOSDTemporarily();
AudioService.sink.audio.volume = newValue / 100;
resetHideTimer();
}
}
onContainsMouseChanged: setChildHovered(containsMouse || muteButton.containsMouse)
onContainsMouseChanged: {
setChildHovered(containsMouse || muteButton.containsMouse);
}
Binding on value {
value: root._displayVolume
when: !volumeSlider.pressed
Connections {
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
function onVolumeChanged() {
if (volumeSlider && !volumeSlider.pressed) {
volumeSlider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
}
}
}
}
}
@@ -142,7 +151,7 @@ DankOSD {
DankIcon {
anchors.centerIn: parent
name: AudioService.sink?.audio?.muted ? "volume_off" : "volume_up"
name: AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.muted ? "volume_off" : "volume_up"
size: Theme.iconSize
color: muteButtonVert.containsMouse ? Theme.primary : Theme.surfaceText
}
@@ -153,8 +162,12 @@ DankOSD {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: AudioService.toggleMute()
onContainsMouseChanged: setChildHovered(containsMouse || vertSliderArea.containsMouse)
onClicked: {
AudioService.toggleMute();
}
onContainsMouseChanged: {
setChildHovered(containsMouse || vertSliderArea.containsMouse);
}
}
}
@@ -166,7 +179,7 @@ DankOSD {
y: gap * 2 + Theme.iconSize
property bool dragging: false
property int value: root._displayVolume
property int value: AudioService.sink && AudioService.sink.audio ? Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100)) : 0
Rectangle {
id: vertTrack
@@ -207,35 +220,50 @@ DankOSD {
id: vertSliderArea
anchors.fill: parent
anchors.margins: -12
enabled: AudioService.sink?.audio
enabled: AudioService.sink && AudioService.sink.audio
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: setChildHovered(containsMouse || muteButtonVert.containsMouse)
onContainsMouseChanged: {
setChildHovered(containsMouse || muteButtonVert.containsMouse);
}
onPressed: mouse => {
vertSlider.dragging = true;
updateVolume(mouse);
}
onReleased: vertSlider.dragging = false
onPositionChanged: mouse => {
if (pressed)
updateVolume(mouse);
onReleased: {
vertSlider.dragging = false;
}
onClicked: mouse => updateVolume(mouse)
onPositionChanged: mouse => {
if (pressed) {
updateVolume(mouse);
}
}
onClicked: mouse => {
updateVolume(mouse);
}
function updateVolume(mouse) {
if (!AudioService.sink?.audio)
return;
const maxVol = AudioService.sinkMaxVolume;
const ratio = 1.0 - (mouse.y / height);
const volume = Math.max(0, Math.min(maxVol, Math.round(ratio * maxVol)));
SessionData.suppressOSDTemporarily();
AudioService.sink.audio.volume = volume / 100;
resetHideTimer();
if (AudioService.sink && AudioService.sink.audio) {
const maxVol = AudioService.sinkMaxVolume;
const ratio = 1.0 - (mouse.y / height);
const volume = Math.max(0, Math.min(maxVol, Math.round(ratio * maxVol)));
SessionData.suppressOSDTemporarily();
AudioService.sink.audio.volume = volume / 100;
resetHideTimer();
}
}
}
Connections {
target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
function onVolumeChanged() {
vertSlider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
}
}
}
@@ -251,4 +279,15 @@ DankOSD {
}
}
}
onOsdShown: {
if (AudioService.sink && AudioService.sink.audio && contentLoader.item && contentLoader.item.item) {
if (!useVertical) {
const slider = contentLoader.item.item.children[0].children[1];
if (slider && slider.value !== undefined) {
slider.value = Math.min(AudioService.sinkMaxVolume, Math.round(AudioService.sink.audio.volume * 100));
}
}
}
}
}
+25 -24
View File
@@ -18,41 +18,36 @@ Column {
property bool isInitialized: false
function loadValue() {
const settings = findSettings();
const settings = findSettings()
if (settings && settings.pluginService) {
const loadedValue = settings.loadValue(settingKey, defaultValue);
if (textField.activeFocus && isInitialized)
return;
value = loadedValue;
textField.text = loadedValue;
isInitialized = true;
const loadedValue = settings.loadValue(settingKey, defaultValue)
value = loadedValue
textField.text = loadedValue
isInitialized = true
}
}
Component.onCompleted: {
Qt.callLater(loadValue);
Qt.callLater(loadValue)
}
function commit() {
if (!isInitialized)
return;
if (textField.text === value)
return;
value = textField.text;
const settings = findSettings();
if (settings)
settings.saveValue(settingKey, value);
onValueChanged: {
if (!isInitialized) return
const settings = findSettings()
if (settings) {
settings.saveValue(settingKey, value)
}
}
function findSettings() {
let item = parent;
let item = parent
while (item) {
if (item.saveValue !== undefined && item.loadValue !== undefined) {
return item;
return item
}
item = item.parent;
item = item.parent
}
return null;
return null
}
StyledText {
@@ -75,10 +70,16 @@ Column {
id: textField
width: parent.width
placeholderText: root.placeholder
onEditingFinished: root.commit()
onTextEdited: {
root.value = text
}
onEditingFinished: {
root.value = text
}
onActiveFocusChanged: {
if (!activeFocus)
root.commit();
if (!activeFocus) {
root.value = text
}
}
}
}
@@ -26,8 +26,8 @@ DankPopout {
open();
}
popupWidth: Math.round(Theme.fontSizeMedium * 46)
popupHeight: Math.round(Theme.fontSizeMedium * 39)
popupWidth: 650
popupHeight: 550
triggerWidth: 55
positioning: ""
screen: triggerScreen
@@ -151,13 +151,11 @@ DankPopout {
DankButtonGroup {
id: processFilterGroup
Layout.minimumWidth: implicitWidth
Layout.minimumWidth: implicitWidth + 8
model: [I18n.tr("All"), I18n.tr("User"), I18n.tr("System")]
currentIndex: 0
checkEnabled: false
buttonHeight: Math.round(Theme.fontSizeSmall * 2.4)
minButtonWidth: 0
buttonPadding: Theme.spacingM
buttonHeight: Math.round(Theme.fontSizeMedium * 2.2)
textSize: Theme.fontSizeSmall
onSelectionChanged: (index, selected) => {
if (!selected)
@@ -179,8 +177,7 @@ DankPopout {
DankTextField {
id: searchField
Layout.fillWidth: true
Layout.minimumWidth: Theme.fontSizeMedium * 8
Layout.preferredWidth: Theme.fontSizeMedium * 14
Layout.preferredHeight: Theme.fontSizeMedium * 2.5
placeholderText: I18n.tr("Search...")
leftIconName: "search"
@@ -1061,7 +1061,7 @@ Singleton {
function getHyprlandOutputIdentifier(output, outputName) {
if (SettingsData.displayNameMode === "model" && output?.make && output?.model)
return "desc:" + output.make + " " + output.model + " " + (output?.serial || "Unknown");
return "desc:" + output.make + " " + output.model;
return outputName;
}
+50 -50
View File
@@ -30,6 +30,56 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
SettingsCard {
width: parent.width
iconName: "swap_vert"
title: I18n.tr("Position")
settingKey: "dockPosition"
Item {
width: parent.width
height: dockPositionButtonGroup.height
DankButtonGroup {
id: dockPositionButtonGroup
anchors.horizontalCenter: parent.horizontalCenter
model: [I18n.tr("Top"), I18n.tr("Bottom"), I18n.tr("Left"), I18n.tr("Right")]
currentIndex: {
switch (SettingsData.dockPosition) {
case SettingsData.Position.Top:
return 0;
case SettingsData.Position.Bottom:
return 1;
case SettingsData.Position.Left:
return 2;
case SettingsData.Position.Right:
return 3;
default:
return 1;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
switch (index) {
case 0:
SettingsData.setDockPosition(SettingsData.Position.Top);
break;
case 1:
SettingsData.setDockPosition(SettingsData.Position.Bottom);
break;
case 2:
SettingsData.setDockPosition(SettingsData.Position.Left);
break;
case 3:
SettingsData.setDockPosition(SettingsData.Position.Right);
break;
}
}
}
}
}
SettingsCard {
width: parent.width
iconName: "dock_to_bottom"
@@ -86,56 +136,6 @@ Item {
}
}
SettingsCard {
width: parent.width
iconName: "swap_vert"
title: I18n.tr("Position")
settingKey: "dockPosition"
Item {
width: parent.width
height: dockPositionButtonGroup.height
DankButtonGroup {
id: dockPositionButtonGroup
anchors.horizontalCenter: parent.horizontalCenter
model: [I18n.tr("Top"), I18n.tr("Bottom"), I18n.tr("Left"), I18n.tr("Right")]
currentIndex: {
switch (SettingsData.dockPosition) {
case SettingsData.Position.Top:
return 0;
case SettingsData.Position.Bottom:
return 1;
case SettingsData.Position.Left:
return 2;
case SettingsData.Position.Right:
return 3;
default:
return 1;
}
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
switch (index) {
case 0:
SettingsData.setDockPosition(SettingsData.Position.Top);
break;
case 1:
SettingsData.setDockPosition(SettingsData.Position.Bottom);
break;
case 2:
SettingsData.setDockPosition(SettingsData.Position.Left);
break;
case 3:
SettingsData.setDockPosition(SettingsData.Position.Right);
break;
}
}
}
}
}
SettingsCard {
width: parent.width
iconName: "apps"
+1 -3
View File
@@ -375,10 +375,8 @@ FocusScope {
if (!plugin || !PluginService.isPluginLoaded(pluginId))
return;
var isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher"));
if (isLauncher) {
pluginsTab.isReloading = true;
if (isLauncher)
PluginService.reloadPlugin(pluginId);
}
}
}
+11 -11
View File
@@ -7,7 +7,7 @@ import qs.Modules.Settings.Widgets
Item {
id: root
readonly property var timeoutOptions: [I18n.tr("Never"), I18n.tr("1 minute"), I18n.tr("2 minutes"), I18n.tr("3 minutes"), I18n.tr("5 minutes"), I18n.tr("10 minutes"), I18n.tr("15 minutes"), I18n.tr("20 minutes"), I18n.tr("30 minutes"), I18n.tr("1 hour"), I18n.tr("1 hour 30 minutes"), I18n.tr("2 hours"), I18n.tr("3 hours")]
readonly property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
readonly property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
function getTimeoutIndex(timeout) {
@@ -56,7 +56,7 @@ Item {
id: powerCategory
anchors.verticalCenter: parent.verticalCenter
visible: BatteryService.batteryAvailable
model: [I18n.tr("AC Power"), I18n.tr("Battery")]
model: ["AC Power", "Battery"]
currentIndex: 0
selectionMode: "single"
checkEnabled: false
@@ -100,7 +100,7 @@ Item {
id: fadeGracePeriodDropdown
settingKey: "fadeToLockGracePeriod"
tags: ["fade", "grace", "period", "timeout", "lock"]
property var periodOptions: [I18n.tr("1 second"), I18n.tr("2 seconds"), I18n.tr("3 seconds"), I18n.tr("4 seconds"), I18n.tr("5 seconds"), I18n.tr("10 seconds"), I18n.tr("15 seconds"), I18n.tr("20 seconds"), I18n.tr("30 seconds")]
property var periodOptions: ["1 second", "2 seconds", "3 seconds", "4 seconds", "5 seconds", "10 seconds", "15 seconds", "20 seconds", "30 seconds"]
property var periodValues: [1, 2, 3, 4, 5, 10, 15, 20, 30]
text: I18n.tr("Lock fade grace period")
@@ -111,7 +111,7 @@ Item {
Component.onCompleted: {
const currentPeriod = SettingsData.fadeToLockGracePeriod;
const index = periodValues.indexOf(currentPeriod);
currentValue = index >= 0 ? periodOptions[index] : I18n.tr("5 seconds");
currentValue = index >= 0 ? periodOptions[index] : "5 seconds";
}
onValueChanged: value => {
@@ -126,7 +126,7 @@ Item {
id: fadeDpmsGracePeriodDropdown
settingKey: "fadeToDpmsGracePeriod"
tags: ["fade", "grace", "period", "timeout", "dpms", "monitor"]
property var periodOptions: [I18n.tr("1 second"), I18n.tr("2 seconds"), I18n.tr("3 seconds"), I18n.tr("4 seconds"), I18n.tr("5 seconds"), I18n.tr("10 seconds"), I18n.tr("15 seconds"), I18n.tr("20 seconds"), I18n.tr("30 seconds")]
property var periodOptions: ["1 second", "2 seconds", "3 seconds", "4 seconds", "5 seconds", "10 seconds", "15 seconds", "20 seconds", "30 seconds"]
property var periodValues: [1, 2, 3, 4, 5, 10, 15, 20, 30]
text: I18n.tr("Monitor fade grace period")
@@ -137,7 +137,7 @@ Item {
Component.onCompleted: {
const currentPeriod = SettingsData.fadeToDpmsGracePeriod;
const index = periodValues.indexOf(currentPeriod);
currentValue = index >= 0 ? periodOptions[index] : I18n.tr("5 seconds");
currentValue = index >= 0 ? periodOptions[index] : "5 seconds";
}
onValueChanged: value => {
@@ -308,7 +308,7 @@ Item {
DankButtonGroup {
id: suspendBehaviorSelector
anchors.horizontalCenter: parent.horizontalCenter
model: [I18n.tr("Suspend"), I18n.tr("Hibernate"), I18n.tr("Suspend then Hibernate")]
model: ["Suspend", "Hibernate", "Suspend then Hibernate"]
selectionMode: "single"
checkEnabled: false
@@ -375,13 +375,13 @@ Item {
settingKey: "powerMenuDefaultAction"
tags: ["power", "menu", "default", "action", "reboot", "logout", "shutdown"]
text: I18n.tr("Default selected action")
options: [I18n.tr("Reboot"), I18n.tr("Log Out"), I18n.tr("Power Off"), I18n.tr("Lock"), I18n.tr("Suspend"), I18n.tr("Restart DMS"), I18n.tr("Hibernate")]
options: ["Reboot", "Log Out", "Power Off", "Lock", "Suspend", "Restart DMS", "Hibernate"]
property var actionValues: ["reboot", "logout", "poweroff", "lock", "suspend", "restart", "hibernate"]
Component.onCompleted: {
const currentAction = SettingsData.powerMenuDefaultAction || "logout";
const index = actionValues.indexOf(currentAction);
currentValue = index >= 0 ? options[index] : I18n.tr("Log Out");
currentValue = index >= 0 ? options[index] : "Log Out";
}
onValueChanged: value => {
@@ -479,7 +479,7 @@ Item {
id: holdDurationDropdown
settingKey: "powerActionHoldDuration"
tags: ["power", "hold", "duration", "confirm", "time"]
property var durationOptions: [I18n.tr("250 ms"), I18n.tr("500 ms"), I18n.tr("750 ms"), I18n.tr("1 second"), I18n.tr("2 seconds"), I18n.tr("3 seconds"), I18n.tr("5 seconds"), I18n.tr("10 seconds")]
property var durationOptions: ["250 ms", "500 ms", "750 ms", "1 second", "2 seconds", "3 seconds", "5 seconds", "10 seconds"]
property var durationValues: [0.25, 0.5, 0.75, 1, 2, 3, 5, 10]
text: I18n.tr("Hold Duration")
@@ -489,7 +489,7 @@ Item {
Component.onCompleted: {
const currentDuration = SettingsData.powerActionHoldDuration;
const index = durationValues.indexOf(currentDuration);
currentValue = index >= 0 ? durationOptions[index] : I18n.tr("500 ms");
currentValue = index >= 0 ? durationOptions[index] : "500 ms";
}
onValueChanged: value => {
+27 -15
View File
@@ -16,7 +16,7 @@ Item {
property var cachedCursorThemes: SettingsData.availableCursorThemes
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
property var installedRegistryThemes: []
property var templateDetection: []
property var templateDetection: ({})
property var cursorIncludeStatus: ({
"exists": false,
@@ -106,10 +106,9 @@ Item {
}
function isTemplateDetected(templateId) {
if (!templateDetection || templateDetection.length === 0)
if (!templateDetection || Object.keys(templateDetection).length === 0)
return true;
var item = templateDetection.find(i => i.id === templateId);
return !item || item.detected !== false;
return templateDetection[templateId] !== false;
}
function getTemplateDescription(templateId, baseDescription) {
@@ -146,17 +145,30 @@ Item {
DMSService.listInstalledThemes();
if (PopoutService.pendingThemeInstall)
Qt.callLater(() => showThemeBrowser());
Proc.runCommand("template-check", ["dms", "matugen", "check"], (output, exitCode) => {
if (exitCode !== 0)
return;
try {
themeColorsTab.templateDetection = JSON.parse(output.trim());
} catch (e) {}
});
templateCheckProcess.running = true;
if (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl)
checkCursorIncludeStatus();
}
Process {
id: templateCheckProcess
command: ["dms", "matugen", "check"]
running: false
stdout: StdioCollector {
onStreamFinished: {
try {
const results = JSON.parse(text);
const detection = {};
for (const item of results) {
detection[item.id] = item.detected;
}
themeColorsTab.templateDetection = detection;
} catch (e) {}
}
}
}
Connections {
target: DMSService
function onInstalledThemesReceived(themes) {
@@ -1033,11 +1045,11 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
model: [
{
"text": I18n.tr("Time", "theme auto mode tab"),
"text": "Time",
"icon": "access_time"
},
{
"text": I18n.tr("Location", "theme auto mode tab"),
"text": "Location",
"icon": "place"
}
]
@@ -2328,7 +2340,7 @@ Item {
tags: ["matugen", "neovim", "terminal", "template"]
settingKey: "matugenTemplateNeovim"
text: "neovim"
description: getTemplateDescription("nvim", I18n.tr("Requires lazy plugin manager", "neovim template description"))
description: getTemplateDescription("nvim", "Requires lazy plugin manager")
descriptionColor: getTemplateDescriptionColor("nvim")
visible: SettingsData.runDmsMatugenTemplates
checked: SettingsData.matugenTemplateNeovim
@@ -2457,7 +2469,7 @@ Item {
onValueChanged: value => {
SettingsData.setIconTheme(value);
if (Quickshell.env("QT_QPA_PLATFORMTHEME") != "gtk3" && Quickshell.env("QT_QPA_PLATFORMTHEME") != "qt6ct" && Quickshell.env("QT_QPA_PLATFORMTHEME_QT6") != "qt6ct") {
ToastService.showError(I18n.tr("Missing Environment Variables", "qt theme env error title"), I18n.tr("You need to set either:\nQT_QPA_PLATFORMTHEME=gtk3 OR\nQT_QPA_PLATFORMTHEME=qt6ct\nas environment variables, and then restart the shell.\n\nqt6ct requires qt6ct-kde to be installed.", "qt theme env error body"));
ToastService.showError("Missing Environment Variables", "You need to set either:\nQT_QPA_PLATFORMTHEME=gtk3 OR\nQT_QPA_PLATFORMTHEME=qt6ct\nas environment variables, and then restart the shell.\n\nqt6ct requires qt6ct-kde to be installed.");
}
}
}
+15 -15
View File
@@ -177,7 +177,7 @@ Item {
if (!PopoutService.colorPickerModal)
return;
PopoutService.colorPickerModal.selectedColor = root.currentWallpaper.startsWith("#") ? root.currentWallpaper : Theme.primary;
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Wallpaper Color", "wallpaper color picker title");
PopoutService.colorPickerModal.pickerTitle = "Choose Wallpaper Color";
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorWallpaper(selectedMonitorName, selectedColor);
@@ -237,7 +237,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
StyledText {
text: root.currentWallpaper ? root.currentWallpaper.split('/').pop() : I18n.tr("No wallpaper selected")
text: root.currentWallpaper ? root.currentWallpaper.split('/').pop() : "No wallpaper selected"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
elide: Text.ElideMiddle
@@ -507,7 +507,7 @@ Item {
return;
var lightWallpaper = SessionData.wallpaperPathLight;
PopoutService.colorPickerModal.selectedColor = lightWallpaper.startsWith("#") ? lightWallpaper : Theme.primary;
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Light Mode Color", "light mode wallpaper color picker title");
PopoutService.colorPickerModal.pickerTitle = "Choose Light Mode Color";
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
SessionData.wallpaperPathLight = selectedColor;
SessionData.syncWallpaperForCurrentMode();
@@ -558,7 +558,7 @@ Item {
StyledText {
text: {
var lightWallpaper = SessionData.wallpaperPathLight;
return lightWallpaper ? lightWallpaper.split('/').pop() : I18n.tr("Not set", "wallpaper not set label");
return lightWallpaper ? lightWallpaper.split('/').pop() : "Not set";
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
@@ -691,7 +691,7 @@ Item {
return;
var darkWallpaper = SessionData.wallpaperPathDark;
PopoutService.colorPickerModal.selectedColor = darkWallpaper.startsWith("#") ? darkWallpaper : Theme.primary;
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Dark Mode Color", "dark mode wallpaper color picker title");
PopoutService.colorPickerModal.pickerTitle = "Choose Dark Mode Color";
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
SessionData.wallpaperPathDark = selectedColor;
SessionData.syncWallpaperForCurrentMode();
@@ -742,7 +742,7 @@ Item {
StyledText {
text: {
var darkWallpaper = SessionData.wallpaperPathDark;
return darkWallpaper ? darkWallpaper.split('/').pop() : I18n.tr("Not set", "wallpaper not set label");
return darkWallpaper ? darkWallpaper.split('/').pop() : "Not set";
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
@@ -813,7 +813,7 @@ Item {
return SettingsData.getScreenDisplayName(screens[i]);
}
}
return I18n.tr("No monitors", "no monitors available label");
return "No monitors";
}
options: {
var screenNames = [];
@@ -844,7 +844,7 @@ Item {
currentValue: {
var screens = Quickshell.screens;
if (!SettingsData.matugenTargetMonitor || SettingsData.matugenTargetMonitor === "") {
return screens.length > 0 ? SettingsData.getScreenDisplayName(screens[0]) + " " + I18n.tr("(Default)", "default monitor label suffix") : I18n.tr("No monitors", "no monitors available label");
return screens.length > 0 ? SettingsData.getScreenDisplayName(screens[0]) + " (Default)" : "No monitors";
}
for (var i = 0; i < screens.length; i++) {
if (screens[i].name === SettingsData.matugenTargetMonitor) {
@@ -859,14 +859,14 @@ Item {
for (var i = 0; i < screens.length; i++) {
var label = SettingsData.getScreenDisplayName(screens[i]);
if (i === 0 && (!SettingsData.matugenTargetMonitor || SettingsData.matugenTargetMonitor === "")) {
label += " " + I18n.tr("(Default)", "default monitor label suffix");
label += " (Default)";
}
screenNames.push(label);
}
return screenNames;
}
onValueChanged: value => {
var cleanValue = value.replace(" " + I18n.tr("(Default)", "default monitor label suffix"), "");
var cleanValue = value.replace(" (Default)", "");
var screens = Quickshell.screens;
for (var i = 0; i < screens.length; i++) {
if (SettingsData.getScreenDisplayName(screens[i]) === cleanValue) {
@@ -941,11 +941,11 @@ Item {
height: 45
model: [
{
"text": I18n.tr("Interval", "wallpaper cycling mode tab"),
"text": "Interval",
"icon": "schedule"
},
{
"text": I18n.tr("Time", "wallpaper cycling mode tab"),
"text": "Time",
"icon": "access_time"
}
]
@@ -981,7 +981,7 @@ Item {
SettingsDropdownRow {
id: intervalDropdown
property var intervalOptions: [I18n.tr("5 seconds", "wallpaper interval"), I18n.tr("10 seconds", "wallpaper interval"), I18n.tr("15 seconds", "wallpaper interval"), I18n.tr("20 seconds", "wallpaper interval"), I18n.tr("25 seconds", "wallpaper interval"), I18n.tr("30 seconds", "wallpaper interval"), I18n.tr("35 seconds", "wallpaper interval"), I18n.tr("40 seconds", "wallpaper interval"), I18n.tr("45 seconds", "wallpaper interval"), I18n.tr("50 seconds", "wallpaper interval"), I18n.tr("55 seconds", "wallpaper interval"), I18n.tr("1 minute", "wallpaper interval"), I18n.tr("5 minutes", "wallpaper interval"), I18n.tr("15 minutes", "wallpaper interval"), I18n.tr("30 minutes", "wallpaper interval"), I18n.tr("1 hour", "wallpaper interval"), I18n.tr("1 hour 30 minutes", "wallpaper interval"), I18n.tr("2 hours", "wallpaper interval"), I18n.tr("3 hours", "wallpaper interval"), I18n.tr("4 hours", "wallpaper interval"), I18n.tr("6 hours", "wallpaper interval"), I18n.tr("8 hours", "wallpaper interval"), I18n.tr("12 hours", "wallpaper interval")]
property var intervalOptions: ["5 seconds", "10 seconds", "15 seconds", "20 seconds", "25 seconds", "30 seconds", "35 seconds", "40 seconds", "45 seconds", "50 seconds", "55 seconds", "1 minute", "5 minutes", "15 minutes", "30 minutes", "1 hour", "1.5 hours", "2 hours", "3 hours", "4 hours", "6 hours", "8 hours", "12 hours"]
property var intervalValues: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 300, 900, 1800, 3600, 5400, 7200, 10800, 14400, 21600, 28800, 43200]
tab: "wallpaper"
@@ -1005,7 +1005,7 @@ Item {
currentSeconds = SessionData.wallpaperCyclingInterval;
}
const index = intervalValues.indexOf(currentSeconds);
return index >= 0 ? intervalOptions[index] : I18n.tr("5 minutes", "wallpaper interval");
return index >= 0 ? intervalOptions[index] : "5 minutes";
}
onValueChanged: value => {
const index = intervalOptions.indexOf(value);
@@ -1029,7 +1029,7 @@ Item {
currentSeconds = SessionData.wallpaperCyclingInterval;
}
const index = intervalDropdown.intervalValues.indexOf(currentSeconds);
intervalDropdown.currentValue = index >= 0 ? intervalDropdown.intervalOptions[index] : I18n.tr("5 minutes", "wallpaper interval");
intervalDropdown.currentValue = index >= 0 ? intervalDropdown.intervalOptions[index] : "5 minutes";
});
}
}
+1 -2
View File
@@ -813,7 +813,7 @@ Item {
}
Rectangle {
width: resetContentRow.implicitWidth + Theme.spacingM * 2
width: 80
height: 28
radius: Theme.cornerRadius
color: resetArea.containsMouse ? Theme.surfacePressed : Theme.surfaceVariant
@@ -821,7 +821,6 @@ Item {
border.width: 0
Row {
id: resetContentRow
anchors.centerIn: parent
spacing: Theme.spacingXS
+8 -12
View File
@@ -242,7 +242,7 @@ Variants {
Image {
id: nextWallpaper
anchors.fill: parent
visible: source !== ""
visible: true
opacity: 0
layer.enabled: false
asynchronous: true
@@ -512,18 +512,14 @@ Variants {
}
}
Loader {
MultiEffect {
anchors.fill: parent
active: CompositorService.isNiri && SettingsData.blurWallpaperOnOverview && NiriService.inOverview && currentWallpaper.source !== ""
sourceComponent: MultiEffect {
anchors.fill: parent
source: effectLoader.active ? effectLoader.item : currentWallpaper
blurEnabled: true
blur: 0.8
blurMax: 75
autoPaddingEnabled: false
}
source: effectLoader.active ? effectLoader.item : currentWallpaper
visible: CompositorService.isNiri && SettingsData.blurWallpaperOnOverview && NiriService.inOverview && currentWallpaper.source !== ""
blurEnabled: true
blur: 0.8
blurMax: 75
autoPaddingEnabled: false
}
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ Singleton {
id: root
readonly property string currentVersion: "1.4"
readonly property bool changelogEnabled: true
readonly property bool changelogEnabled: false
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/DankMaterialShell"
readonly property string changelogMarkerPath: configDir + "/.changelog-" + currentVersion
+8 -9
View File
@@ -288,9 +288,9 @@ Singleton {
const cpu = data.cpu;
cpuSampleCount++;
cpuUsage = Math.round((cpu.usage || 0) * 10) / 10;
cpuFrequency = Math.round(cpu.frequency || 0);
cpuTemperature = Math.round(cpu.temperature || 0);
cpuUsage = cpu.usage || 0;
cpuFrequency = cpu.frequency || 0;
cpuTemperature = cpu.temperature || 0;
cpuCores = cpu.count || 1;
cpuModel = cpu.model || "";
perCoreCpuUsage = cpu.coreUsage || [];
@@ -308,12 +308,11 @@ Singleton {
const freeKB = mem.free || 0;
const usedKB = mem.used !== undefined ? mem.used : (totalKB - availableKB);
totalMemoryMB = Math.round(totalKB / 1024);
availableMemoryMB = Math.round(availableKB / 1024);
freeMemoryMB = Math.round(freeKB / 1024);
usedMemoryMB = Math.round(usedKB / 1024);
const rawMemUsage = mem.usedPercent !== undefined ? mem.usedPercent : (totalKB > 0 ? ((totalKB - availableKB) / totalKB) * 100 : 0);
memoryUsage = Math.round(rawMemUsage * 10) / 10;
totalMemoryMB = totalKB / 1024;
availableMemoryMB = availableKB / 1024;
freeMemoryMB = freeKB / 1024;
usedMemoryMB = usedKB / 1024;
memoryUsage = mem.usedPercent !== undefined ? mem.usedPercent : (totalKB > 0 ? ((totalKB - availableKB) / totalKB) * 100 : 0);
totalMemoryKB = totalKB;
usedMemoryKB = usedKB;
+1 -1
View File
@@ -55,7 +55,7 @@ Singleton {
function getOutputIdentifier(output, outputName) {
if (SettingsData.displayNameMode === "model" && output.make && output.model)
return "desc:" + output.make + " " + output.model + " " + (output.serial || "Unknown");
return "desc:" + output.make + " " + output.model;
return outputName;
}
+98 -41
View File
@@ -963,34 +963,48 @@ Singleton {
return enrichedToplevels;
}
function _matchAndEnrichToplevels(toplevels, niriWindows) {
function filterCurrentWorkspace(toplevels, screenName) {
let currentWorkspaceId = null;
for (var i = 0; i < allWorkspaces.length; i++) {
const ws = allWorkspaces[i];
if (ws.output === screenName && ws.is_active) {
currentWorkspaceId = ws.id;
break;
}
}
if (currentWorkspaceId === null)
return toplevels;
const workspaceWindows = windows.filter(niriWindow => niriWindow.workspace_id === currentWorkspaceId);
const usedToplevels = new Set();
const result = [];
for (const niriWindow of niriWindows) {
for (const niriWindow of workspaceWindows) {
let bestMatch = null;
let bestScore = -1;
for (const toplevel of toplevels) {
if (usedToplevels.has(toplevel))
continue;
if (toplevel.appId !== niriWindow.app_id)
continue;
if (toplevel.appId === niriWindow.app_id) {
let score = 1;
let score = 1;
if (niriWindow.title && toplevel.title) {
if (toplevel.title === niriWindow.title) {
score = 3;
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
score = 2;
if (niriWindow.title && toplevel.title) {
if (toplevel.title === niriWindow.title) {
score = 3;
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
score = 2;
}
}
}
if (score > bestScore) {
bestScore = score;
bestMatch = toplevel;
if (score === 3)
break;
if (score > bestScore) {
bestScore = score;
bestMatch = toplevel;
if (score === 3)
break;
}
}
}
@@ -1011,15 +1025,17 @@ Singleton {
return NiriService.focusWindow(niriWindow.id);
},
"close": function () {
if (bestMatch.close)
if (bestMatch.close) {
return bestMatch.close();
}
return false;
}
};
for (let prop in bestMatch) {
if (!(prop in enrichedToplevel))
if (!(prop in enrichedToplevel)) {
enrichedToplevel[prop] = bestMatch[prop];
}
}
result.push(enrichedToplevel);
@@ -1028,26 +1044,6 @@ Singleton {
return result;
}
function filterCurrentWorkspace(toplevels, screenName) {
let currentWorkspaceId = null;
for (var i = 0; i < allWorkspaces.length; i++) {
const ws = allWorkspaces[i];
if (ws.output === screenName && ws.is_active) {
currentWorkspaceId = ws.id;
break;
}
}
if (currentWorkspaceId === null)
return toplevels;
if (toplevels.length > 0 && toplevels[0].niriWorkspaceId !== undefined)
return toplevels.filter(t => t.niriWorkspaceId === currentWorkspaceId);
return _matchAndEnrichToplevels(toplevels, windows.filter(nw => nw.workspace_id === currentWorkspaceId));
}
function filterCurrentDisplay(toplevels, screenName) {
if (!toplevels || toplevels.length === 0 || !screenName)
return toplevels;
@@ -1062,10 +1058,71 @@ Singleton {
if (outputWorkspaceIds.size === 0)
return toplevels;
if (toplevels.length > 0 && toplevels[0].niriWorkspaceId !== undefined)
return toplevels.filter(t => outputWorkspaceIds.has(t.niriWorkspaceId));
const displayWindows = windows.filter(niriWindow => outputWorkspaceIds.has(niriWindow.workspace_id));
const usedToplevels = new Set();
const result = [];
return _matchAndEnrichToplevels(toplevels, windows.filter(nw => outputWorkspaceIds.has(nw.workspace_id)));
for (const niriWindow of displayWindows) {
let bestMatch = null;
let bestScore = -1;
for (const toplevel of toplevels) {
if (usedToplevels.has(toplevel))
continue;
if (toplevel.appId === niriWindow.app_id) {
let score = 1;
if (niriWindow.title && toplevel.title) {
if (toplevel.title === niriWindow.title) {
score = 3;
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
score = 2;
}
}
if (score > bestScore) {
bestScore = score;
bestMatch = toplevel;
if (score === 3)
break;
}
}
}
if (!bestMatch)
continue;
usedToplevels.add(bestMatch);
const workspace = workspaces[niriWindow.workspace_id];
const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false;
const enrichedToplevel = {
"appId": bestMatch.appId,
"title": bestMatch.title,
"activated": isFocused,
"niriWindowId": niriWindow.id,
"niriWorkspaceId": niriWindow.workspace_id,
"activate": function () {
return NiriService.focusWindow(niriWindow.id);
},
"close": function () {
if (bestMatch.close) {
return bestMatch.close();
}
return false;
}
};
for (let prop in bestMatch) {
if (!(prop in enrichedToplevel)) {
enrichedToplevel[prop] = bestMatch[prop];
}
}
result.push(enrichedToplevel);
}
return result;
}
function generateNiriLayoutConfig() {
+21 -54
View File
@@ -16,7 +16,6 @@ Singleton {
property var registeredCards: ({})
property var settingsIndex: []
property bool indexLoaded: false
property var _translatedCache: []
readonly property var conditionMap: ({
"isNiri": () => CompositorService.isNiri,
@@ -39,11 +38,9 @@ Singleton {
try {
root.settingsIndex = JSON.parse(text());
root.indexLoaded = true;
root._rebuildTranslationCache();
} catch (e) {
console.warn("SettingsSearchService: Failed to parse index:", e);
root.settingsIndex = [];
root._translatedCache = [];
}
}
onLoadFailed: error => console.warn("SettingsSearchService: Failed to load index:", error)
@@ -134,27 +131,6 @@ Singleton {
};
}
function _rebuildTranslationCache() {
var cache = [];
for (var i = 0; i < settingsIndex.length; i++) {
var item = settingsIndex[i];
var t = translateItem(item);
cache.push({
section: t.section,
label: t.label,
tabIndex: t.tabIndex,
category: t.category,
keywords: t.keywords,
icon: t.icon,
description: t.description,
conditionKey: t.conditionKey,
labelLower: t.label.toLowerCase(),
categoryLower: t.category.toLowerCase()
});
}
_translatedCache = cache;
}
function search(text) {
query = text;
if (!text) {
@@ -162,19 +138,18 @@ Singleton {
return;
}
var queryLower = text.toLowerCase().trim();
var queryWords = queryLower.split(/\s+/).filter(w => w.length > 0);
var scored = [];
var cache = _translatedCache;
const queryLower = text.toLowerCase().trim();
const queryWords = queryLower.split(/\s+/).filter(w => w.length > 0);
const scored = [];
for (var i = 0; i < cache.length; i++) {
var entry = cache[i];
if (!checkCondition(entry))
for (const item of settingsIndex) {
if (!checkCondition(item))
continue;
var labelLower = entry.labelLower;
var categoryLower = entry.categoryLower;
var score = 0;
const translated = translateItem(item);
const labelLower = translated.label.toLowerCase();
const categoryLower = translated.category.toLowerCase();
let score = 0;
if (labelLower === queryLower) {
score = 10000;
@@ -187,32 +162,24 @@ Singleton {
}
if (score === 0) {
var keywords = entry.keywords;
for (var k = 0; k < keywords.length; k++) {
if (keywords[k].startsWith(queryLower)) {
score = 800;
for (const keyword of item.keywords) {
if (keyword.startsWith(queryLower)) {
score = Math.max(score, 800);
break;
}
if (keywords[k].includes(queryLower) && score < 400) {
score = 400;
if (keyword.includes(queryLower)) {
score = Math.max(score, 400);
}
}
}
if (score === 0 && queryWords.length > 1) {
var allMatch = true;
for (var w = 0; w < queryWords.length; w++) {
var word = queryWords[w];
if (labelLower.includes(word))
continue;
var inKeywords = false;
for (var k = 0; k < entry.keywords.length; k++) {
if (entry.keywords[k].includes(word)) {
inKeywords = true;
break;
}
}
if (!inKeywords && !categoryLower.includes(word)) {
let allMatch = true;
for (const word of queryWords) {
const inLabel = labelLower.includes(word);
const inKeywords = item.keywords.some(k => k.includes(word));
const inCategory = categoryLower.includes(word);
if (!inLabel && !inKeywords && !inCategory) {
allMatch = false;
break;
}
@@ -223,7 +190,7 @@ Singleton {
if (score > 0) {
scored.push({
item: entry,
item: translated,
score: score
});
}
+33 -33
View File
@@ -119,36 +119,36 @@ Singleton {
function getWeatherCondition(code) {
const conditions = {
"0": I18n.tr("Clear Sky"),
"1": I18n.tr("Clear Sky"),
"2": I18n.tr("Partly Cloudy"),
"3": I18n.tr("Overcast"),
"45": I18n.tr("Fog"),
"48": I18n.tr("Fog"),
"51": I18n.tr("Drizzle"),
"53": I18n.tr("Drizzle"),
"55": I18n.tr("Drizzle"),
"56": I18n.tr("Freezing Drizzle"),
"57": I18n.tr("Freezing Drizzle"),
"61": I18n.tr("Light Rain"),
"63": I18n.tr("Rain"),
"65": I18n.tr("Heavy Rain"),
"66": I18n.tr("Light Rain"),
"67": I18n.tr("Heavy Rain"),
"71": I18n.tr("Light Snow"),
"73": I18n.tr("Snow"),
"75": I18n.tr("Heavy Snow"),
"77": I18n.tr("Snow"),
"80": I18n.tr("Light Rain"),
"81": I18n.tr("Rain"),
"82": I18n.tr("Heavy Rain"),
"85": I18n.tr("Light Snow Showers"),
"86": I18n.tr("Heavy Snow Showers"),
"95": I18n.tr("Thunderstorm"),
"96": I18n.tr("Thunderstorm with Hail"),
"99": I18n.tr("Thunderstorm with Hail")
"0": "Clear",
"1": "Clear",
"2": "Partly cloudy",
"3": "Overcast",
"45": "Fog",
"48": "Fog",
"51": "Drizzle",
"53": "Drizzle",
"55": "Drizzle",
"56": "Freezing drizzle",
"57": "Freezing drizzle",
"61": "Light rain",
"63": "Rain",
"65": "Heavy rain",
"66": "Light rain",
"67": "Heavy rain",
"71": "Light snow",
"73": "Snow",
"75": "Heavy snow",
"77": "Snow",
"80": "Light rain",
"81": "Rain",
"82": "Heavy rain",
"85": "Light snow showers",
"86": "Heavy snow showers",
"95": "Thunderstorm",
"96": "Thunderstorm with hail",
"99": "Thunderstorm with hail"
};
return conditions[String(code)] || I18n.tr("Unknown");
return conditions[String(code)] || "Unknown";
}
property var moonPhaseNames: ["moon_new", "moon_waxing_crescent", "moon_first_quarter", "moon_waxing_gibbous", "moon_full", "moon_waning_gibbous", "moon_last_quarter", "moon_waning_crescent"]
@@ -647,8 +647,8 @@ Singleton {
const address = data.address || {};
root.location = {
city: address.hamlet || address.city || address.town || address.village || I18n.tr("Unknown"),
country: address.country || I18n.tr("Unknown"),
city: address.hamlet || address.city || address.town || address.village || "Unknown",
country: address.country || "Unknown",
latitude: parseFloat(data.lat),
longitude: parseFloat(data.lon)
};
@@ -807,8 +807,8 @@ Singleton {
"tempF": Math.round(tempF),
"feelsLike": Math.round(feelsLikeC),
"feelsLikeF": Math.round(feelsLikeF),
"city": root.location?.city || I18n.tr("Unknown"),
"country": root.location?.country || I18n.tr("Unknown"),
"city": root.location?.city || "Unknown",
"country": root.location?.country || "Unknown",
"wCode": current.weather_code || 0,
"humidity": Math.round(current.relative_humidity_2m || 0),
"wind": Math.round(current.wind_speed_10m || 0),
+1 -1
View File
@@ -1 +1 @@
v1.4.2
v1.5-beta
+1 -5
View File
@@ -28,10 +28,6 @@ PanelWindow {
function show() {
if (SessionData.suppressOSD)
return;
if (shouldBeVisible) {
hideTimer.restart();
return;
}
OSDManager.showOSD(root);
closeTimer.stop();
shouldBeVisible = true;
@@ -261,7 +257,7 @@ PanelWindow {
property real shadowSpreadPx: 0
property real shadowBaseAlpha: 0.60
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
readonly property real effectiveShadowAlpha: shouldBeVisible ? Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) : 0
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha * osdContainer.opacity))
Rectangle {
id: background
+22 -46
View File
@@ -109,13 +109,6 @@ Item {
readonly property bool useBackgroundWindow: !CompositorService.isHyprland || CompositorService.useHyprlandFocusGrab
function updateSurfacePosition() {
if (useBackgroundWindow && shouldBeVisible) {
_surfaceMarginLeft = alignedX - shadowBuffer;
_surfaceW = alignedWidth + shadowBuffer * 2;
}
}
function open() {
if (!screen)
return;
@@ -476,44 +469,6 @@ Item {
}
}
Rectangle {
id: shadowSource
anchors.centerIn: parent
width: parent.width
height: parent.height
radius: Theme.cornerRadius
color: "black"
visible: false
opacity: contentWrapper.opacity
scale: contentWrapper.scale
x: contentWrapper.x
y: contentWrapper.y
property real shadowBlurPx: 10
property real shadowSpreadPx: 0
property real shadowBaseAlpha: 0.60
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
readonly property int blurMax: 64
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive)
layer.smooth: false
layer.effect: MultiEffect {
id: shadowFx
autoPaddingEnabled: true
shadowEnabled: true
blurEnabled: false
maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, shadowSource.shadowBlurPx / shadowSource.blurMax))
shadowScale: 1 + (2 * shadowSource.shadowSpreadPx) / Math.max(1, Math.min(shadowSource.width, shadowSource.height))
shadowColor: {
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
return Theme.withAlpha(baseColor, shadowSource.effectiveShadowAlpha);
}
}
}
Item {
id: contentWrapper
anchors.centerIn: parent
@@ -525,10 +480,31 @@ Item {
x: Theme.snap(contentContainer.animX + (parent.width - width) * (1 - contentContainer.scaleValue) * 0.5, root.dpr)
y: Theme.snap(contentContainer.animY + (parent.height - height) * (1 - contentContainer.scaleValue) * 0.5, root.dpr)
layer.enabled: contentWrapper.opacity < 1
property real shadowBlurPx: 10
property real shadowSpreadPx: 0
property real shadowBaseAlpha: 0.60
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
readonly property int blurMax: 64
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive)
layer.smooth: false
layer.textureSize: root.dpr > 1 ? Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr)) : Qt.size(0, 0)
layer.effect: MultiEffect {
id: shadowFx
autoPaddingEnabled: true
shadowEnabled: true
blurEnabled: false
maskEnabled: false
shadowBlur: Math.max(0, Math.min(1, contentWrapper.shadowBlurPx / contentWrapper.blurMax))
shadowScale: 1 + (2 * contentWrapper.shadowSpreadPx) / Math.max(1, Math.min(contentWrapper.width, contentWrapper.height))
shadowColor: {
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
return Theme.withAlpha(baseColor, contentWrapper.effectiveShadowAlpha);
}
}
Behavior on opacity {
NumberAnimation {
duration: animationDuration
+1 -1
View File
@@ -1,3 +1,3 @@
[templates.dmsemacs]
input_path = 'SHELL_DIR/matugen/templates/dank-emacs.el'
output_path = 'EMACS_DIR/themes/dank-emacs-theme.el'
output_path = "~/.emacs.d/themes/dank-emacs-theme.el"
File diff suppressed because it is too large Load Diff
-190
View File
@@ -104,30 +104,18 @@
"2 minutes": {
"2 minutes": "2 minutos"
},
"2 seconds": {
"2 seconds": ""
},
"20 minutes": {
"20 minutes": ""
},
"24-Hour Format": {
"24-Hour Format": "Formato de 24 horas"
},
"24-hour format": {
"24-hour format": "Formato de 24 horas"
},
"250 ms": {
"250 ms": ""
},
"270°": {
"270°": "270º"
},
"3 days": {
"3 days": "3 dias"
},
"3 minutes": {
"3 minutes": ""
},
"3 seconds": {
"3 seconds": "3 segundos"
},
@@ -140,24 +128,15 @@
"3rd party": {
"3rd party": "De terceros"
},
"4 seconds": {
"4 seconds": ""
},
"5 minutes": {
"5 minutes": "5 minutos"
},
"5 seconds": {
"5 seconds": "5 segundos"
},
"500 ms": {
"500 ms": ""
},
"7 days": {
"7 days": "7 días"
},
"750 ms": {
"750 ms": ""
},
"8 seconds": {
"8 seconds": "8 segundos"
},
@@ -170,9 +149,6 @@
"A file with this name already exists. Do you want to overwrite it?": {
"A file with this name already exists. Do you want to overwrite it?": "Un archivo con este nombre ya existe. ¿Quieres reemplazarlo?"
},
"AC Power": {
"AC Power": ""
},
"API": {
"API": "API"
},
@@ -769,9 +745,6 @@
"Caps Lock Indicator": {
"Caps Lock Indicator": "Indicador de Bloq Mayús"
},
"Caps Lock is on": {
"Caps Lock is on": ""
},
"Center Section": {
"Center Section": "Sección central"
},
@@ -862,9 +835,6 @@
"Clear History?": {
"Clear History?": ""
},
"Clear Sky": {
"Clear Sky": ""
},
"Clear all history when server starts": {
"Clear all history when server starts": "Limpiar todo el historial cuando el servidor inicia"
},
@@ -1417,9 +1387,6 @@
"Description": {
"Description": "Descripción"
},
"Desktop": {
"Desktop": ""
},
"Desktop Clock": {
"Desktop Clock": "Reloj del escritorio"
},
@@ -1591,9 +1558,6 @@
"Docs": {
"Docs": "Docs"
},
"Documents": {
"Documents": ""
},
"Domain (optional)": {
"Domain (optional)": "Dominio (opcional)"
},
@@ -1606,9 +1570,6 @@
"Door Open": {
"Door Open": "Puera abierta"
},
"Downloads": {
"Downloads": ""
},
"Drag to Reorder": {
"Drag to Reorder": ""
},
@@ -1621,9 +1582,6 @@
"Driver": {
"Driver": "Controlador"
},
"Drizzle": {
"Drizzle": ""
},
"Duplicate": {
"Duplicate": "Duplicado"
},
@@ -2086,9 +2044,6 @@
"Focused Window": {
"Focused Window": "Ventana enfocada"
},
"Fog": {
"Fog": ""
},
"Follow Monitor Focus": {
"Follow Monitor Focus": ""
},
@@ -2152,9 +2107,6 @@
"Free VRAM/memory when the launcher is closed. May cause a slight delay when reopening.": {
"Free VRAM/memory when the launcher is closed. May cause a slight delay when reopening.": ""
},
"Freezing Drizzle": {
"Freezing Drizzle": ""
},
"Frequency": {
"Frequency": "Frecuencia"
},
@@ -2260,15 +2212,6 @@
"Health": {
"Health": "Salud"
},
"Heavy Rain": {
"Heavy Rain": ""
},
"Heavy Snow": {
"Heavy Snow": ""
},
"Heavy Snow Showers": {
"Heavy Snow Showers": ""
},
"Height": {
"Height": ""
},
@@ -2368,9 +2311,6 @@
"Hold to confirm (%1s)": {
"Hold to confirm (%1s)": "Mantener pulsado para confirmar (%1s)"
},
"Home": {
"Home": ""
},
"Hot Corners": {
"Hot Corners": "Esquinas calientes"
},
@@ -2818,15 +2758,6 @@
"Light Mode": {
"Light Mode": "Modo claro"
},
"Light Rain": {
"Light Rain": ""
},
"Light Snow": {
"Light Snow": ""
},
"Light Snow Showers": {
"Light Snow Showers": ""
},
"Linear": {
"Linear": "Lineal"
},
@@ -3172,9 +3103,6 @@
"Moving to Paused": {
"Moving to Paused": "Pausando"
},
"Music": {
"Music": ""
},
"Mute popups for %1": {
"Mute popups for %1": ""
},
@@ -3592,9 +3520,6 @@
"Outputs Include Missing": {
"Outputs Include Missing": "Los resultados incluyen"
},
"Overcast": {
"Overcast": ""
},
"Overflow": {
"Overflow": ""
},
@@ -3652,9 +3577,6 @@
"Pairing...": {
"Pairing...": "Vinculando..."
},
"Partly Cloudy": {
"Partly Cloudy": ""
},
"Passkey:": {
"Passkey:": "Clave de acceso:"
},
@@ -3762,9 +3684,6 @@
"Phone Connect unavailable status": {
"Unavailable": ""
},
"Pictures": {
"Pictures": ""
},
"Pin": {
"Pin": "Fijar"
},
@@ -3990,9 +3909,6 @@
"Protocol": {
"Protocol": "Protocolo"
},
"Quick Access": {
"Quick Access": ""
},
"Quick access to application launcher": {
"Quick access to application launcher": "Acceso rápido al lanzador de aplicaciones"
},
@@ -4011,9 +3927,6 @@
"Radius": {
"Radius": ""
},
"Rain": {
"Rain": ""
},
"Rain Chance": {
"Rain Chance": "Lluvia"
},
@@ -4713,9 +4626,6 @@
"Snap": {
"Snap": ""
},
"Snow": {
"Snow": ""
},
"Some plugins require a newer version of DMS:": {
"Some plugins require a newer version of DMS:": "Algunos plugins requieren una version más nueva de DMS:"
},
@@ -4812,9 +4722,6 @@
"Suspend system after": {
"Suspend system after": "Suspender sistema después de"
},
"Suspend then Hibernate": {
"Suspend then Hibernate": ""
},
"Swap": {
"Swap": "Intercambio"
},
@@ -4953,12 +4860,6 @@
"This will permanently remove this saved clipboard item. This action cannot be undone.": {
"This will permanently remove this saved clipboard item. This action cannot be undone.": ""
},
"Thunderstorm": {
"Thunderstorm": ""
},
"Thunderstorm with Hail": {
"Thunderstorm with Hail": ""
},
"Tiled": {
"Tiled": ""
},
@@ -5349,9 +5250,6 @@
"Vibrant palette with playful saturation.": {
"Vibrant palette with playful saturation.": "Paleta vibrante y juguetona."
},
"Videos": {
"Videos": ""
},
"View Mode": {
"View Mode": ""
},
@@ -5621,9 +5519,6 @@
"border thickness": {
"Thickness": ""
},
"brandon": {
"brandon": ""
},
"browse themes button | theme browser header | theme browser window title": {
"Browse Themes": "Busqueda de temas"
},
@@ -5667,9 +5562,6 @@
"custom theme file hint": {
"Click to select a custom theme JSON file": "Haz clic para seleccionar un archivo JSON de tema personalizado"
},
"dark mode wallpaper color picker title": {
"Choose Dark Mode Color": ""
},
"dark mode wallpaper file browser title | light mode wallpaper file browser title | wallpaper file browser title": {
"Select Wallpaper": "Seleccionar fondo de pantalla"
},
@@ -5688,9 +5580,6 @@
"days": {
"days": "dias"
},
"default monitor label suffix": {
"(Default)": ""
},
"dgop not available": {
"dgop not available": "dgop no disponible"
},
@@ -5956,9 +5845,6 @@
"leave empty for default": {
"leave empty for default": "deja vacio para valor por defecto"
},
"light mode wallpaper color picker title": {
"Choose Light Mode Color": ""
},
"loading indicator": {
"Loading...": "Cargando..."
},
@@ -6018,9 +5904,6 @@
"nav": {
"nav": ""
},
"neovim template description": {
"Requires lazy plugin manager": ""
},
"network status": {
"Connected": "",
"Disabling WiFi...": "",
@@ -6034,9 +5917,6 @@
"no custom theme file status": {
"No custom theme file": "Ningún archivo de tema personalizado"
},
"no monitors available label": {
"No monitors": ""
},
"no registry themes installed hint": {
"No themes installed. Browse themes to install from the registry.": "No hay temas instalados. Busca temas para instalar en el repositorio/tienda de complementos."
},
@@ -6117,24 +5997,6 @@
"official": {
"official": "oficial"
},
"on Hyprland": {
"on Hyprland": ""
},
"on MangoWC": {
"on MangoWC": ""
},
"on Miracle WM": {
"on Miracle WM": ""
},
"on Niri": {
"on Niri": ""
},
"on Scroll": {
"on Scroll": ""
},
"on Sway": {
"on Sway": ""
},
"open": {
"open": ""
},
@@ -6182,12 +6044,6 @@
"profile image file browser title": {
"Select Profile Image": "Elegir foto de perfil"
},
"qt theme env error body": {
"You need to set either:\nQT_QPA_PLATFORMTHEME=gtk3 OR\nQT_QPA_PLATFORMTHEME=qt6ct\nas environment variables, and then restart the shell.\n\nqt6ct requires qt6ct-kde to be installed.": ""
},
"qt theme env error title": {
"Missing Environment Variables": ""
},
"read-only settings warning for NixOS home-manager users": {
"Settings are read-only. Changes will not persist.": "Los ajustes son de solo lectura. Los cambios no persistirán."
},
@@ -6245,12 +6101,6 @@
"Kernel": "",
"Load Average": ""
},
"theme auto mode tab": {
"Location": ""
},
"theme auto mode tab | wallpaper cycling mode tab": {
"Time": ""
},
"theme browser description": {
"Install color themes from the DMS theme registry": "Instalar tema de colores desde el repositorio de temas de DMS"
},
@@ -6291,9 +6141,6 @@
"unknown author": {
"Unknown": "Desconocido"
},
"up": {
"up": ""
},
"update dms for NM integration.": {
"update dms for NM integration.": "Actualizar dms para integración con NM."
},
@@ -6306,12 +6153,6 @@
"version requirement": {
"Requires %1": ""
},
"wallpaper color picker title": {
"Choose Wallpaper Color": ""
},
"wallpaper cycling mode tab": {
"Interval": ""
},
"wallpaper directory file browser title": {
"Select Wallpaper Directory": "Elegir carpeta de fondos de pantalla"
},
@@ -6330,34 +6171,6 @@
"Tile H": "",
"Tile V": ""
},
"wallpaper interval": {
"1 hour": "",
"1 hour 30 minutes": "",
"1 minute": "",
"10 seconds": "",
"12 hours": "",
"15 minutes": "",
"15 seconds": "",
"2 hours": "",
"20 seconds": "",
"25 seconds": "",
"3 hours": "",
"30 minutes": "",
"30 seconds": "",
"35 seconds": "",
"4 hours": "",
"40 seconds": "",
"45 seconds": "",
"5 minutes": "",
"5 seconds": "",
"50 seconds": "",
"55 seconds": "",
"6 hours": "",
"8 hours": ""
},
"wallpaper not set label": {
"Not set": ""
},
"wallpaper processing error": {
"Wallpaper processing failed": "Fallo en el procesamiento del fondo de pantalla"
},
@@ -6432,8 +6245,5 @@
},
"• yyyy - Year (2024)": {
"• yyyy - Year (2024)": "yyyy - Año (2024)"
},
"↑/↓: Nav • Space: Expand • Enter: Action/Expand • E: Text": {
"↑/↓: Nav • Space: Expand • Enter: Action/Expand • E: Text": ""
}
}
+78 -268
View File
@@ -104,30 +104,18 @@
"2 minutes": {
"2 minutes": "۲ دقیقه"
},
"2 seconds": {
"2 seconds": "۲ ثانیه"
},
"20 minutes": {
"20 minutes": "۲۰ دقیقه"
},
"24-Hour Format": {
"24-Hour Format": "قالب ۲۴ ساعته"
},
"24-hour format": {
"24-hour format": "قالب ۲۴ ساعته"
},
"250 ms": {
"250 ms": "۲۵۰ ms"
},
"270°": {
"270°": "۲۷۰°"
},
"3 days": {
"3 days": "۳ روز"
},
"3 minutes": {
"3 minutes": "۳ دقیقه"
},
"3 seconds": {
"3 seconds": "۳ ثانیه"
},
@@ -140,24 +128,15 @@
"3rd party": {
"3rd party": "شخص ثالث"
},
"4 seconds": {
"4 seconds": "۴ ثانیه"
},
"5 minutes": {
"5 minutes": "‍۵ دقیقه"
},
"5 seconds": {
"5 seconds": "۵ ثانیه"
},
"500 ms": {
"500 ms": "۵۰۰ ms"
},
"7 days": {
"7 days": "۷ روز"
},
"750 ms": {
"750 ms": "۷۵۰ ms"
},
"8 seconds": {
"8 seconds": "۸ ثانیه"
},
@@ -170,9 +149,6 @@
"A file with this name already exists. Do you want to overwrite it?": {
"A file with this name already exists. Do you want to overwrite it?": "فایلی با این نام از قبل وجود دارد. آیا می‌خواهید آن را بازنویسی کنید؟"
},
"AC Power": {
"AC Power": "برق AC"
},
"API": {
"API": "API"
},
@@ -381,7 +357,7 @@
"Apps with custom display name, icon, or launch options. Right-click an app and select 'Edit App' to customize.": "برنامه‌ها با نام نمایشی، آیکون و گزینه‌های اجرای سفارشی. روی یک برنامه راست‌کلیک کرده و «ویرایش برنامه» را برای سفارشی‌سازی انتخاب کنید."
},
"Apps with notification popups muted. Unmute or delete to remove.": {
"Apps with notification popups muted. Unmute or delete to remove.": "برنامه‌هایی که پاپ‌آپ اعلان آن‌ها بی‌صدا شده است. برای حذف، بی‌صدا را بردارید یا برنامه را حذف کنید."
"Apps with notification popups muted. Unmute or delete to remove.": ""
},
"Arrange displays and configure resolution, refresh rate, and VRR": {
"Arrange displays and configure resolution, refresh rate, and VRR": "چیدمان نمایشگرها و پیکربندی وضوح، نرخ تازه‌سازی و VRR"
@@ -524,7 +500,7 @@
"Automatically cycle through wallpapers in the same folder": "تصاویر پس‌زمینه را در همان پوشه به صورت خودکار تغییر بده"
},
"Automatically delete entries older than this": {
"Automatically delete entries older than this": "مدخل‌های قدیمی‌تر از این را به صورت خودکار پاک کن"
"Automatically delete entries older than this": "ورودی‌های قدیمی‌تر از این را به صورت خودکار پاک کن"
},
"Automatically detect location based on IP address": {
"Automatically detect location based on IP address": "تشخیص خودکار موقعیت مکانی بر اساس آدرس IP"
@@ -587,7 +563,7 @@
"Bar Transparency": "شفافیت نوار"
},
"Base duration for animations (drag to use Custom)": {
"Base duration for animations (drag to use Custom)": "مدت زمان پایه برای انیمیشن‌ها (برای استفاده سفارشی بکشید)"
"Base duration for animations (drag to use Custom)": ""
},
"Battery": {
"Battery": "باتری"
@@ -617,10 +593,10 @@
"Bit Depth": "عمق بیت"
},
"Block Out": {
"Block Out": "مسدود کردن خروج"
"Block Out": ""
},
"Block Out From": {
"Block Out From": "مسدود کردن خروج از"
"Block Out From": ""
},
"Block notifications": {
"Block notifications": "مسدود‌کردن اعلان‌ها"
@@ -665,7 +641,7 @@
"Border Thickness": "ضخامت حاشیه"
},
"Border with BG": {
"Border with BG": "حاشیه با پس‌زمینه"
"Border with BG": ""
},
"Bottom": {
"Bottom": "پایین"
@@ -737,7 +713,7 @@
"CUPS not available": "CUPS در دسترس نیست"
},
"Calc": {
"Calc": "ماشین حساب"
"Calc": ""
},
"Calculator": {
"Calculator": "ماشین حساب"
@@ -769,9 +745,6 @@
"Caps Lock Indicator": {
"Caps Lock Indicator": "نشانگر Caps Lock"
},
"Caps Lock is on": {
"Caps Lock is on": ""
},
"Center Section": {
"Center Section": "بخش مرکزی"
},
@@ -839,7 +812,7 @@
"Choose which monitor shows the lock screen interface. Other monitors will display a solid color for OLED burn-in protection.": "انتخاب کنید که کدام مانیتور رابط صفحه قفل را نشان دهد. مانیتورهای دیگر برای محافظت در برابر سوختگی OLED، رنگ ثابتی را نمایش می‌دهند."
},
"Chroma Style": {
"Chroma Style": "استایل رنگی"
"Chroma Style": ""
},
"Cipher": {
"Cipher": "رمزگذار"
@@ -862,9 +835,6 @@
"Clear History?": {
"Clear History?": "تاریخچه پاک شود؟"
},
"Clear Sky": {
"Clear Sky": "آسمان پاک"
},
"Clear all history when server starts": {
"Clear all history when server starts": "هنگام راه‌اندازی سرور تمام تاریخچه را پاک کن"
},
@@ -920,7 +890,7 @@
"Clipboard Manager": "مدیریت کلیپ‌بورد"
},
"Clipboard behavior setting description": {
"Press Enter to paste, Shift+Enter to copy": "برای الصاق Enter، برای کپی Shift+Enter را فشار دهید"
"Press Enter to paste, Shift+Enter to copy": ""
},
"Clipboard service not available": {
"Clipboard service not available": "سرویس کلیپ‌بورد در دسترس نیست"
@@ -980,16 +950,16 @@
"Color temperature for night mode": "دمای رنگ برای حالت شب"
},
"Color theme for syntax highlighting.": {
"Color theme for syntax highlighting.": "تم رنگی برای برجسته‌سازی کد."
"Color theme for syntax highlighting.": ""
},
"Color theme for syntax highlighting. %1 themes available.": {
"Color theme for syntax highlighting. %1 themes available.": "تم رنگی برای برجسته‌سازی کد. %1 تم دردسترس است."
"Color theme for syntax highlighting. %1 themes available.": ""
},
"Colorful mix of bright contrasting accents.": {
"Colorful mix of bright contrasting accents.": "ترکیب رنگارنگ از رنگ‌های تأکیدی متضاد."
},
"Colorize Active": {
"Colorize Active": "رنگ‌آمیزی حالت فعال"
"Colorize Active": ""
},
"Column": {
"Column": "ستون"
@@ -1052,7 +1022,7 @@
"Configure icons for named workspaces. Icons take priority over numbers when both are enabled.": "آیکون‌ها را برای محیط‌کارهای نام‌دار تنظیم کنید. آیکون‌ها بر شماره‌ها هنگامی که هر دو فعال باشند اولویت دارند."
},
"Configure match criteria and actions": {
"Configure match criteria and actions": "پیکربندی انطباق معیار و اقدامات"
"Configure match criteria and actions": "پیکربندی معیارهای انطباق و اقدامات"
},
"Configure which displays show \"%1": {
"Configure which displays show \"%1\"": "تنظیم کنید که کدام نمایشگر‌ها «%1» را نشان دهند"
@@ -1112,7 +1082,7 @@
"Control Center Tile Color": "رنگ کاشی مرکز کنترل"
},
"Control animation duration for notification popups and history": {
"Control animation duration for notification popups and history": "مدت زمان انیمیشن را برای پاپ‌آپ اعلان‌ها و تاریخچه مشخص کنید"
"Control animation duration for notification popups and history": ""
},
"Control currently playing media": {
"Control currently playing media": "کنترل رسانه درحال پخش"
@@ -1205,7 +1175,7 @@
"Create rules to mute, ignore, hide from history, or override notification priority.": "برای بی‌صدا کردن، نادیده گرفتن، پنهان‌سازی از تاریخچه و یا تغییر اولویت اعلان قاعده ایجاد کنید."
},
"Create rules to mute, ignore, hide from history, or override notification priority. Default only overrides priority; notifications still show normally.": {
"Create rules to mute, ignore, hide from history, or override notification priority. Default only overrides priority; notifications still show normally.": "برای بی‌صدا کردن، نادیده گرفتن، مخفی‌سازی از تاریخچه، یا تغییر اولویت اعلان‌ها قواعدی ایجاد کنید. در حالت پیش‌فرض، تنها اولویت تغییر می‌کند؛ اعلان‌ها همچنان به‌صورت عادی نمایش داده می‌شوند."
"Create rules to mute, ignore, hide from history, or override notification priority. Default only overrides priority; notifications still show normally.": ""
},
"Creating...": {
"Creating...": "درحال ایجاد..."
@@ -1241,7 +1211,7 @@
"Cursor Config Not Configured": "پیکربندی اشاره‌گر موس تنظیم نشده"
},
"Cursor Include Missing": {
"Cursor Include Missing": "دستور include اشاره‌گر موس یافت نشد"
"Cursor Include Missing": ""
},
"Cursor Size": {
"Cursor Size": "اندازه اشاره‌گر موس"
@@ -1322,7 +1292,7 @@
"DWL service not available": "سرویس DWL در دسترس نیست"
},
"Daily": {
"Daily": "روزانه"
"Daily": ""
},
"Daily Forecast": {
"Daily Forecast": "پیش‌بینی روزانه"
@@ -1417,9 +1387,6 @@
"Description": {
"Description": "توضیحات"
},
"Desktop": {
"Desktop": "دسکتاپ"
},
"Desktop Clock": {
"Desktop Clock": "ساعت دسکتاپ"
},
@@ -1591,9 +1558,6 @@
"Docs": {
"Docs": "مستندات"
},
"Documents": {
"Documents": "اسناد"
},
"Domain (optional)": {
"Domain (optional)": "دامنه (اختیاری)"
},
@@ -1606,9 +1570,6 @@
"Door Open": {
"Door Open": "درب باز"
},
"Downloads": {
"Downloads": "بارگیری‌ها"
},
"Drag to Reorder": {
"Drag to Reorder": "نگه‌داشتن برای بازآرایی"
},
@@ -1621,9 +1582,6 @@
"Driver": {
"Driver": "درایور"
},
"Drizzle": {
"Drizzle": "باران‌ریزه"
},
"Duplicate": {
"Duplicate": "تکثیر"
},
@@ -1631,7 +1589,7 @@
"Duplicate Wallpaper with Blur": "تصویر پس‌زمینه تکراری با تاری"
},
"Duration": {
"Duration": "مدت زمان"
"Duration": ""
},
"Dusk (Astronomical Twilight)": {
"Dusk (Astronomical Twilight)": "غروب (گرگ و میش نجومی)"
@@ -1784,16 +1742,16 @@
"Enter this passkey on ": "ورود این کلید عبور در "
},
"Enter to Paste": {
"Enter to Paste": "Enter برای الصاق"
"Enter to Paste": ""
},
"Enterprise": {
"Enterprise": "شرکت"
},
"Entry pinned": {
"Entry pinned": "مدخل سنجاق شد"
"Entry pinned": "ورودی سنجاق شد"
},
"Entry unpinned": {
"Entry unpinned": "سنجاق مدخل برداشته شد"
"Entry unpinned": "سنجاق ورودی برداشته شد"
},
"Environment Variables": {
"Environment Variables": "متغیر‌های محیطی"
@@ -1814,7 +1772,7 @@
"Exponential": "نمایی"
},
"Extra Arguments": {
"Extra Arguments": "آرگومان‌های اضافی"
"Extra Arguments": ""
},
"F1/I: Toggle • F10: Help": {
"F1/I: Toggle • F10: Help": "F1/I: تغییر حالت • F10: راهنما"
@@ -1856,7 +1814,7 @@
"Failed to connect to %1": "اتصال به %1 ناموفق بود"
},
"Failed to copy entry": {
"Failed to copy entry": "کپی مدخل ناموفق بود"
"Failed to copy entry": "کپی ورودی ناموفق بود"
},
"Failed to create printer": {
"Failed to create printer": "ایجاد چاپگر ناموفق بود"
@@ -1928,7 +1886,7 @@
"Failed to pause printer": "توقف چاپگر ناموفق بود"
},
"Failed to pin entry": {
"Failed to pin entry": "سنجاق کردن مدخل ناموفق بود"
"Failed to pin entry": "سنجاق کردن ورودی ناموفق بود"
},
"Failed to print test page": {
"Failed to print test page": "چاپ صفحه تست ناموفق بود"
@@ -1985,7 +1943,7 @@
"Failed to start connection to %1": "شروع اتصال به %1 ناموفق بود"
},
"Failed to unpin entry": {
"Failed to unpin entry": "برداشتن سنجاق مدخل ناموفق بود"
"Failed to unpin entry": "برداشتن سنجاق ورودی ناموفق بود"
},
"Failed to update VPN": {
"Failed to update VPN": "بروزرسانی VPN ناموفق بود"
@@ -2086,9 +2044,6 @@
"Focused Window": {
"Focused Window": "پنجره فوکوس‌شده"
},
"Fog": {
"Fog": "مه"
},
"Follow Monitor Focus": {
"Follow Monitor Focus": "فوکوس مانیتور را دنبال کن"
},
@@ -2147,13 +2102,10 @@
"Forgot network %1": "فراموشی شبکه %1"
},
"Format Legend": {
"Format Legend": "راهنمای قالب‌بندی"
"Format Legend": "قالب فهرست علائم"
},
"Free VRAM/memory when the launcher is closed. May cause a slight delay when reopening.": {
"Free VRAM/memory when the launcher is closed. May cause a slight delay when reopening.": "هنگامی که لانچر بسته شد VRAM/حافظه را آزاد کن. ممکن است باعث اندکی تأخیر هنگام باز کردن مجدد شود."
},
"Freezing Drizzle": {
"Freezing Drizzle": "باران‌ریزه یخ‌زن"
"Free VRAM/memory when the launcher is closed. May cause a slight delay when reopening.": ""
},
"Frequency": {
"Frequency": "فرکانس"
@@ -2260,15 +2212,6 @@
"Health": {
"Health": "سلامت"
},
"Heavy Rain": {
"Heavy Rain": "باران سنگین"
},
"Heavy Snow": {
"Heavy Snow": "برف سنگین"
},
"Heavy Snow Showers": {
"Heavy Snow Showers": "برف سنگین پراکنده"
},
"Height": {
"Height": "ارتفاع"
},
@@ -2288,7 +2231,7 @@
"Hidden": "پنهان"
},
"Hidden (%1)": {
"Hidden (%1)": "پنهان (%1)"
"Hidden (%1)": ""
},
"Hidden Apps": {
"Hidden Apps": "برنامه‌های پنهان"
@@ -2330,13 +2273,13 @@
"Hide cursor when using touch input": "اشاره‌گر موس را هنگام استفاده از ورودی لمسی پنهان کن"
},
"Hide device": {
"Hide device": "پنهان کردن دستگاه"
"Hide device": ""
},
"Hide notification content until expanded": {
"Hide notification content until expanded": "پنهان کردن محتوای اعلان را تا هنگام گسترش"
"Hide notification content until expanded": ""
},
"Hide notification content until expanded; popups show collapsed by default": {
"Hide notification content until expanded; popups show collapsed by default": "محتوای اعلان را تا هنگام گسترش پنهان کن؛ پاپ‌آپ‌ها به طور پیش‌فرض گسترش‌یافته نمایش داده می‌شوند"
"Hide notification content until expanded; popups show collapsed by default": ""
},
"Hide on Touch": {
"Hide on Touch": "هنگام لمس پنهان کن"
@@ -2351,7 +2294,7 @@
"History Settings": "تنظیمات تاریخچه"
},
"History cleared. %1 pinned entries kept.": {
"History cleared. %1 pinned entries kept.": "تاریخچه پاک شد. %1 مدخل سنجاق شده نگه داشته شد."
"History cleared. %1 pinned entries kept.": "تاریخچه پاک شد. %1 ورودی سنجاق شده نگه داشته شد."
},
"Hold Duration": {
"Hold Duration": "مدت زمان نگه‌داشتن"
@@ -2368,9 +2311,6 @@
"Hold to confirm (%1s)": {
"Hold to confirm (%1s)": "برای تأیید نگه‌دارید (%1s)"
},
"Home": {
"Home": "خانه"
},
"Hot Corners": {
"Hot Corners": "گوشه‌های فعال"
},
@@ -2731,13 +2671,13 @@
"Key": "کلید"
},
"Keybind Sources": {
"Keybind Sources": "منابع نگاشت‌کلیدها"
"Keybind Sources": ""
},
"Keybinds Search Settings": {
"Keybinds Search Settings": "تنظیمات نگاشت‌کلیدهای جستجو"
},
"Keybinds shown alongside regular search results": {
"Keybinds shown alongside regular search results": "نگاشت‌کلیدها همراه نتایج عادی جستجو نمایش داده می‌شوند"
"Keybinds shown alongside regular search results": ""
},
"Keyboard Layout Name": {
"Keyboard Layout Name": "نام چیدمان صفحه‌کلید"
@@ -2746,8 +2686,8 @@
"Keyboard Shortcuts": "میانبر‌های صفحه‌کلید"
},
"Keyboard hints when enter-to-paste is enabled": {
"Shift+Enter: Copy • Shift+Del: Clear All • Esc: Close": "Shift+Enter: کپی • Shift+Del: پاک‌کردن همه • Esc: بستن",
"↑/↓: Navigate • Enter: Paste • Del: Delete • F10: Help": "↑/↓: پیمایش • Enter: الصاق • Del: حذف • F10: راهنما"
"Shift+Enter: Copy • Shift+Del: Clear All • Esc: Close": "",
"↑/↓: Navigate • Enter: Paste • Del: Delete • F10: Help": ""
},
"Keys": {
"Keys": "کلیدها"
@@ -2818,15 +2758,6 @@
"Light Mode": {
"Light Mode": "حالت روشن"
},
"Light Rain": {
"Light Rain": "باران سبک"
},
"Light Snow": {
"Light Snow": "برف سبک"
},
"Light Snow Showers": {
"Light Snow Showers": "برف پراکنده"
},
"Linear": {
"Linear": "خطی"
},
@@ -2963,10 +2894,10 @@
"Marker Waste Full": "ضایعات مارکر پر است"
},
"Match Criteria": {
"Match Criteria": "انطباق معیار"
"Match Criteria": ""
},
"Matches profile: %1": {
"Matches profile: %1": "منطبق با پروفایل: %1"
"Matches profile: %1": ""
},
"Material Colors": {
"Material Colors": "رنگ‌های Material"
@@ -3008,31 +2939,31 @@
"Max to Edges": "بیشینه تا لبه‌ها"
},
"Maximize": {
"Maximize": "بزرگ‌کردن پنجره"
"Maximize": ""
},
"Maximize Detection": {
"Maximize Detection": "تشخیص بزرگ‌شدن پنجره"
},
"Maximum Entry Size": {
"Maximum Entry Size": "حداکثر اندازه مدخل"
"Maximum Entry Size": "حداکثر اندازه ورودی"
},
"Maximum History": {
"Maximum History": "حداکثر تاریخچه"
},
"Maximum Pinned Entries": {
"Maximum Pinned Entries": "بیشینه مدخل‌های سنجاق شده"
"Maximum Pinned Entries": "بیشینه ورودی‌های سنجاق شده"
},
"Maximum number of clipboard entries to keep": {
"Maximum number of clipboard entries to keep": "بیشینه تعداد مدخل‌های کلیپ‌بورد برای نگه‌داری"
"Maximum number of clipboard entries to keep": "بیشینه تعداد ورودی‌های کلیپ‌بورد برای نگه‌داری"
},
"Maximum number of entries that can be saved": {
"Maximum number of entries that can be saved": "بیشینه تعداد مدخل‌هایی که می‌توانند ذخیره شوند"
"Maximum number of entries that can be saved": "بیشینه تعداد ورودی‌هایی که می‌توانند ذخیره شوند"
},
"Maximum pinned entries reached": {
"Maximum pinned entries reached": "به بیشینه مدخل‌های سنجاق شده رسیدید"
"Maximum pinned entries reached": "به بیشینه ورودی‌های سنجاق شده رسیدید"
},
"Maximum size per clipboard entry": {
"Maximum size per clipboard entry": "بیشینه اندازه برای هر مدخل کلیپ‌بورد"
"Maximum size per clipboard entry": "بیشینه اندازه برای هر ورودی کلیپ‌بورد"
},
"Media": {
"Media": "رسانه"
@@ -3053,7 +2984,7 @@
"Media Needed": "مدیا مورد نیاز است"
},
"Media Playback": {
"Media Playback": "پخش رسانه"
"Media Playback": ""
},
"Media Player": {
"Media Player": "پخش‌کننده رسانه"
@@ -3172,14 +3103,11 @@
"Moving to Paused": {
"Moving to Paused": "انتقال به حالت متوقف شده"
},
"Music": {
"Music": "موسیقی"
},
"Mute popups for %1": {
"Mute popups for %1": "بی‌صدا کردن پاپ‌آپ‌ها را برای %1"
"Mute popups for %1": ""
},
"Muted Apps": {
"Muted Apps": "برنامه‌های بی‌صدا"
"Muted Apps": ""
},
"Muted palette with subdued, calming tones.": {
"Muted palette with subdued, calming tones.": "پالت رنگی ساکت با تن‌های ملایم و آرامش‌بخش."
@@ -3332,7 +3260,7 @@
"No apps have been launched yet.": "هیچ برنامه‌ای هنوز اجرا نشده است."
},
"No apps muted. Right-click a notification and choose \"Mute popups\" to add one here.": {
"No apps muted. Right-click a notification and choose \"Mute popups\" to add one here.": "هیچ برنامه‌ای بی‌صدا نشده است. برای افزودن یکی به اینجا روی اعلان راست‌کلیک کرده و «بی‌صدا کردن پاپ‌آپ‌ها» را انتخاب کنید."
"No apps muted. Right-click a notification and choose \"Mute popups\" to add one here.": ""
},
"No battery": {
"No battery": "بدون باتری"
@@ -3344,7 +3272,7 @@
"No changes": "بدون تغییرات"
},
"No clipboard entries found": {
"No clipboard entries found": "هیچ مدخل کلیپ‌بوردی پیدا نشد"
"No clipboard entries found": "هیچ ورودی کلیپ‌بوردی پیدا نشد"
},
"No devices found": {
"No devices found": "دستگاهی یافت نشد"
@@ -3377,7 +3305,7 @@
"No launcher plugins installed.": "هیچ افزونه‌ لانچری نصب نشده است."
},
"No match criteria": {
"No match criteria": "هیچ انطباق معیاری یافت نشد"
"No match criteria": ""
},
"No matches": {
"No matches": "موردی پیدا نشد"
@@ -3404,13 +3332,13 @@
"No profiles": "هیچ پروفایلی یافت نشد"
},
"No recent clipboard entries found": {
"No recent clipboard entries found": "هیچ مدخل کلیپ‌بورد اخیری یافت نشد"
"No recent clipboard entries found": "هیچ ورودی کلیپ‌بورد اخیری یافت نشد"
},
"No results found": {
"No results found": "هیچ نتیجه‌ای یافت نشد"
},
"No saved clipboard entries": {
"No saved clipboard entries": "هیچ مدخل کلیپ‌بوردی ذخیره نشده است"
"No saved clipboard entries": "هیچ ورودی کلیپ‌بوردی ذخیره نشده است"
},
"No trigger": {
"No trigger": "بدون راه‌انداز"
@@ -3539,7 +3467,7 @@
"Opacity": "شفافیت"
},
"Opaque": {
"Opaque": "کدر"
"Opaque": ""
},
"Open": {
"Open": "باز‌کردن"
@@ -3592,9 +3520,6 @@
"Outputs Include Missing": {
"Outputs Include Missing": "Include خروجی‌ها یافت نشد"
},
"Overcast": {
"Overcast": "کاملاْ ابری"
},
"Overflow": {
"Overflow": "سرریزی"
},
@@ -3632,7 +3557,7 @@
"PIN": "PIN"
},
"Pad Hours": {
"Pad Hours": "حاشیه‌گذاری ساعات"
"Pad Hours": ""
},
"Padding": {
"Padding": "فاصله درونی"
@@ -3652,9 +3577,6 @@
"Pairing...": {
"Pairing...": "درحال جفت شدن..."
},
"Partly Cloudy": {
"Partly Cloudy": "نیمه ابری"
},
"Passkey:": {
"Passkey:": "کلید عبور:"
},
@@ -3717,7 +3639,7 @@
"Failed to send ping": "ارسال پینگ ناموفق بود",
"Failed to share": "اشتراک‌گذاری ناموفق بود",
"Pairing failed": "جفت‌سازی ناموفق بود",
"Unpair failed": "جدا‌سازی ناموفق بود"
"Unpair failed": ""
},
"Phone Connect file send": {
"Sending": "درحال ارسال"
@@ -3762,9 +3684,6 @@
"Phone Connect unavailable status": {
"Unavailable": "دردسترس نیست"
},
"Pictures": {
"Pictures": "تصاویر"
},
"Pin": {
"Pin": "سنجاق"
},
@@ -3835,7 +3754,7 @@
"Popup Position": "مکان پاپ‌آپ"
},
"Popup Shadow": {
"Popup Shadow": "سایه پاپ‌آپ"
"Popup Shadow": ""
},
"Popup Transparency": {
"Popup Transparency": "شفافیت پاپ‌آپ"
@@ -3946,7 +3865,7 @@
"Privacy Indicator": "نشانگر حریم خصوصی"
},
"Privacy Mode": {
"Privacy Mode": "حالت حریم خصوصی"
"Privacy Mode": ""
},
"Private Key Password": {
"Private Key Password": "کلید خصوصی گذرواژه"
@@ -3990,9 +3909,6 @@
"Protocol": {
"Protocol": "پروتکل"
},
"Quick Access": {
"Quick Access": "دسترسی سریع"
},
"Quick access to application launcher": {
"Quick access to application launcher": "دسترسی سریع به لانچر برنامه"
},
@@ -4011,9 +3927,6 @@
"Radius": {
"Radius": "شعاع"
},
"Rain": {
"Rain": "باران"
},
"Rain Chance": {
"Rain Chance": "احتمال بارش"
},
@@ -4282,10 +4195,10 @@
"Search Options": "گزینه‌های جستجو"
},
"Search by key combo, description, or action name.\n\nDefault action copies the keybind to clipboard.\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": {
"Search by key combo, description, or action name.\n\nDefault action copies the keybind to clipboard.\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": "با ترکیب کلید‌ها، توضیحات یا نام اقدام‌ جستجو کنید.\n\nاقدام پیش‌فرض نگاشت‌کلید را در کلیپ‌بورد کپی می‌کند.\nبرای سنجاق‌ کردن نگاشت‌کلید‌های پر استفاده راست‌کلیک کنید یا پیکان راست را فشار دهید - آن‌ها هنگام جستجو نکردن در بالای لیست ظاهر خواهند شد."
"Search by key combo, description, or action name.\n\nDefault action copies the keybind to clipboard.\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": ""
},
"Search by key combo, description, or action name.\\n\\nDefault action copies the keybind to clipboard.\\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": {
"Search by key combo, description, or action name.\\n\\nDefault action copies the keybind to clipboard.\\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": "با ترکیب کلید‌ها، توضیحات یا نام اقدام‌ جستجو کنید.\\n\\اقدام پیش‌فرض نگاشت‌کلید را در کلیپ‌بورد کپی می‌کند.\\nبرای سنجاق‌ کردن نگاشت‌کلید‌های پر استفاده راست‌کلیک کنید یا پیکان راست را فشار دهید - آن‌ها هنگام جستجو نکردن در بالای لیست ظاهر خواهند شد."
"Search by key combo, description, or action name.\\n\\nDefault action copies the keybind to clipboard.\\nRight-click or press Right Arrow to pin frequently used keybinds - they'll appear at the top when not searching.": "با ترکیب کلید‌ها، توضیحات یا نام اقدام‌ جستجو کنید.\\n\\اقدام پیش‌فرض نگاشت‌کلید را در کلیپ‌بورد کپی می‌کند.\\nبرای سنجاق‌ کردن نگاشت‌کلید‌های پر استفاده راست‌کلیک کنید یا پیکان راست را فشار دهید - آن‌ها هنگام جستجو نکردن در بالا ظاهر خواهند شد."
},
"Search file contents": {
"Search file contents": "جستجو در محتوای فایل"
@@ -4333,7 +4246,7 @@
"Select Bar": "انتخاب نوار"
},
"Select Dock Launcher Logo": {
"Select Dock Launcher Logo": "لوگوی لانچر داک را انتخاب کنید"
"Select Dock Launcher Logo": ""
},
"Select Launcher Logo": {
"Select Launcher Logo": "انتخاب لوگوی لانچر"
@@ -4381,7 +4294,7 @@
"Select the palette algorithm used for wallpaper-based colors": "انتخاب الگوریتم پالت رنگی استفاده شده برای رنگ‌های بر اساس تصویر پس‌زمینه"
},
"Select which keybind providers to include": {
"Select which keybind providers to include": "انتخاب کنید که کدام ارائه دهنده نگاشت‌کلید‌ها include شود"
"Select which keybind providers to include": ""
},
"Select which transitions to include in randomization": {
"Select which transitions to include in randomization": "انتخاب کنید کدام گذارها در تصادفی‌سازی باشند"
@@ -4411,13 +4324,13 @@
"Set key and action to save": "کلید و اقدام را برای ذخیره تنظیم کنید"
},
"Set notification rules": {
"Set notification rules": "تنظیم قوانین اعلان‌ها"
"Set notification rules": ""
},
"Setup": {
"Setup": "راه‌اندازی"
},
"Share Gamma Control Settings": {
"Share Gamma Control Settings": "اشتراک‌گذاری تنظیمات کنترل گاما"
"Share Gamma Control Settings": ""
},
"Shell": {
"Shell": "شِل"
@@ -4585,13 +4498,13 @@
"Show darkened overlay behind modal dialogs": "لایه overlay تیره پشت پنجره مودال نمایش بده"
},
"Show device": {
"Show device": "نمایش دستگاه"
"Show device": ""
},
"Show dock when floating windows don't overlap its area": {
"Show dock when floating windows don't overlap its area": "داک را هنگامی که پنجره‌های شناور با محیط آن همپوشانی ندارند نمایش بده"
},
"Show drop shadow on notification popups": {
"Show drop shadow on notification popups": "سایه‌افتاده را برای پاپ‌آپ اعلان‌ها نمایش بده"
"Show drop shadow on notification popups": ""
},
"Show launcher overlay when typing in Niri overview. Disable to use another launcher.": {
"Show launcher overlay when typing in Niri overview. Disable to use another launcher.": "لایه overlay لانچر را هنگام تایپ در نمای کلی نیری نمایش بده. برای استفاده از لانچر دیگری غیرفعال کنید."
@@ -4690,7 +4603,7 @@
"Size": "اندازه"
},
"Size Constraints": {
"Size Constraints": "محدودیت‌های اندازه"
"Size Constraints": ""
},
"Size Offset": {
"Size Offset": "آفست اندازه"
@@ -4713,9 +4626,6 @@
"Snap": {
"Snap": "چسباندن"
},
"Snow": {
"Snow": "برف"
},
"Some plugins require a newer version of DMS:": {
"Some plugins require a newer version of DMS:": "برخی افزونه‌ها نیازمند نسخه جدیدتر DMS هستند:"
},
@@ -4812,9 +4722,6 @@
"Suspend system after": {
"Suspend system after": "تعلیق پس از"
},
"Suspend then Hibernate": {
"Suspend then Hibernate": "تعلیق سپس هایبرنیت"
},
"Swap": {
"Swap": "سواپ"
},
@@ -4945,7 +4852,7 @@
"This widget prevents GPU power off states, which can significantly impact battery life on laptops. It is not recommended to use this on laptops with hybrid graphics.": "این ابزارک از حالت‌های خاموش شدن GPU جلوگیری می‌کند، که ​​می‌تواند به طور قابل توجهی بر عمر باتری لپ‌تاپ‌ها تأثیر بگذارد. استفاده از این ابزارک در لپ‌تاپ‌هایی با گرافیک هیبریدی توصیه نمی‌شود."
},
"This will delete all unpinned entries. %1 pinned entries will be kept.": {
"This will delete all unpinned entries. %1 pinned entries will be kept.": "این همه مدخل‌های سنجاق نشده را پاک می‌کند. %1 مدخل سنجاق شده نگه داشته می‌شوند."
"This will delete all unpinned entries. %1 pinned entries will be kept.": "این همه ورودی‌های سنجاق نشده را پاک می‌کند. %1 ورودی سنجاق شده نگه داشته می‌شوند."
},
"This will permanently delete all clipboard history.": {
"This will permanently delete all clipboard history.": "این کار تمام تاریخچه کلیپ‌بورد را برای همیشه حذف می‌کند."
@@ -4953,17 +4860,11 @@
"This will permanently remove this saved clipboard item. This action cannot be undone.": {
"This will permanently remove this saved clipboard item. This action cannot be undone.": "این به طور دائمی این مورد کلیپ‌بورد را حذف خواهد کرد. این عمل برگشت‌ناپذیر است."
},
"Thunderstorm": {
"Thunderstorm": "رعد و برق"
},
"Thunderstorm with Hail": {
"Thunderstorm with Hail": "رعد و برق با تگرگ"
},
"Tiled": {
"Tiled": "کاشی‌شده"
"Tiled": ""
},
"Tiled State": {
"Tiled State": "حالت کاشی‌شده"
"Tiled State": ""
},
"Tiling": {
"Tiling": "تایلینگ"
@@ -5143,13 +5044,13 @@
"Unknown Title": "عنوان ناشناس"
},
"Unload on Close": {
"Unload on Close": "خالی‌کردن هنگام بستن"
"Unload on Close": ""
},
"Unmute": {
"Unmute": "صدادار کردن"
"Unmute": ""
},
"Unmute popups for %1": {
"Unmute popups for %1": "صدادار کردن پاپ‌آپ‌ها را برای %1"
"Unmute popups for %1": ""
},
"Unnamed Rule": {
"Unnamed Rule": "قاعده بی‌نام"
@@ -5349,9 +5250,6 @@
"Vibrant palette with playful saturation.": {
"Vibrant palette with playful saturation.": "پالتر رنگی پر جنب و جوش با اشباع رنگی سرزنده."
},
"Videos": {
"Videos": "ویدئوها"
},
"View Mode": {
"View Mode": "حالت نمایش"
},
@@ -5621,9 +5519,6 @@
"border thickness": {
"Thickness": "ضخامت"
},
"brandon": {
"brandon": "براندون"
},
"browse themes button | theme browser header | theme browser window title": {
"Browse Themes": "مرور تم‌ها"
},
@@ -5653,7 +5548,7 @@
"Active tile background and icon color": "رنگ کاشی پس‌زمینه فعال و آیکون"
},
"count of hidden audio devices": {
"Hidden (%1)": "پنهان (%1)"
"Hidden (%1)": ""
},
"current theme label": {
"Current Theme: %1": "تم کنونی: %1"
@@ -5667,9 +5562,6 @@
"custom theme file hint": {
"Click to select a custom theme JSON file": "برای انتخاب یک فایل JSON قالب سفارشی کلیک کنید"
},
"dark mode wallpaper color picker title": {
"Choose Dark Mode Color": "انتخاب رنگ حالت تاریک"
},
"dark mode wallpaper file browser title | light mode wallpaper file browser title | wallpaper file browser title": {
"Select Wallpaper": "انتخاب تصویر پس‌زمینه"
},
@@ -5688,9 +5580,6 @@
"days": {
"days": "روز"
},
"default monitor label suffix": {
"(Default)": "(پیش‌فرض)"
},
"dgop not available": {
"dgop not available": "dgop در دسترس نیست"
},
@@ -5757,7 +5646,7 @@
"No GPUs detected": "هیچ GPUای شناسایی نشد"
},
"empty state in process list": {
"No matching processes": "فرآیند موردنظر یافت نشد"
"No matching processes": ""
},
"empty theme list": {
"No themes found": "هیچ تمی یافت نشد"
@@ -5956,9 +5845,6 @@
"leave empty for default": {
"leave empty for default": "برای پیش‌فرض خالی رها کنید"
},
"light mode wallpaper color picker title": {
"Choose Light Mode Color": "انتخاب رنگ حالت روشن"
},
"loading indicator": {
"Loading...": "درحال بارگذاری..."
},
@@ -6018,9 +5904,6 @@
"nav": {
"nav": "جهت"
},
"neovim template description": {
"Requires lazy plugin manager": "به مدیریت افزونه lazy نیاز دارد"
},
"network status": {
"Connected": "متصل",
"Disabling WiFi...": "غیرفعال‌سازی وای‌فای...",
@@ -6034,9 +5917,6 @@
"no custom theme file status": {
"No custom theme file": "هیچ تم سفارشی یافت نشد"
},
"no monitors available label": {
"No monitors": "بدون مانیتور"
},
"no registry themes installed hint": {
"No themes installed. Browse themes to install from the registry.": "هیچ تمی نصب نشده. تم‌ها را برای نصب از مخزن مرور کنید."
},
@@ -6085,7 +5965,7 @@
"Enable History": "فعال‌کردن تاریخچه"
},
"notification privacy mode placeholder": {
"Message Content": "محتوای پیام"
"Message Content": ""
},
"notification rule action option": {
"Ignore Completely": "کلاً نادیده بگیر",
@@ -6098,7 +5978,7 @@
},
"notification rule match field option": {
"Body": "بدنه",
"Desktop Entry": "مدخل دسکتاپ",
"Desktop Entry": "",
"Summary": "خلاصه"
},
"notification rule match type option": {
@@ -6117,24 +5997,6 @@
"official": {
"official": "رسمی"
},
"on Hyprland": {
"on Hyprland": "در Hyprland"
},
"on MangoWC": {
"on MangoWC": "در MangoWC"
},
"on Miracle WM": {
"on Miracle WM": "در Miracle WM"
},
"on Niri": {
"on Niri": "در Niri"
},
"on Scroll": {
"on Scroll": "در Scroll"
},
"on Sway": {
"on Sway": "در Sway"
},
"open": {
"open": "باز کردن"
},
@@ -6182,12 +6044,6 @@
"profile image file browser title": {
"Select Profile Image": "انتخاب تصویر نمایه"
},
"qt theme env error body": {
"You need to set either:\nQT_QPA_PLATFORMTHEME=gtk3 OR\nQT_QPA_PLATFORMTHEME=qt6ct\nas environment variables, and then restart the shell.\n\nqt6ct requires qt6ct-kde to be installed.": "لازم است یکی از مقادیر زیر را به عنوان متغیر محیطی تنظیم نمایید:\nQT_QPA_PLATFORMTHEME=gtk3 یا\nQT_QPA_PLATFORMTHEME=qt6ct\nو پس از آن، شل را مجدداً راه‌اندازی کنید.\n\nبسته qt6ct نیازمند نصب qt6ct-kde است."
},
"qt theme env error title": {
"Missing Environment Variables": "متغیر‌های محیطی یافت نشد"
},
"read-only settings warning for NixOS home-manager users": {
"Settings are read-only. Changes will not persist.": "تنظیمات فقط قابل خواندن هستند. تغییرات حفظ نخواهند شد."
},
@@ -6245,12 +6101,6 @@
"Kernel": "کرنل",
"Load Average": "متوسط بارگذاری"
},
"theme auto mode tab": {
"Location": "مکان"
},
"theme auto mode tab | wallpaper cycling mode tab": {
"Time": "زمان"
},
"theme browser description": {
"Install color themes from the DMS theme registry": "نصب تم رنگ‌ها از مخزن تم DMS"
},
@@ -6270,7 +6120,7 @@
"Search themes...": "جستجوی تم‌ها..."
},
"this app": {
"this app": "این برنامه"
"this app": ""
},
"tile color option": {
"Primary Container": "زمینه اصلی",
@@ -6291,9 +6141,6 @@
"unknown author": {
"Unknown": "ناشناخته"
},
"up": {
"up": "روشن"
},
"update dms for NM integration.": {
"update dms for NM integration.": "DMS را برای یکپارچه‌سازی NM بروز کنید."
},
@@ -6306,12 +6153,6 @@
"version requirement": {
"Requires %1": "به %1 نیاز دارد"
},
"wallpaper color picker title": {
"Choose Wallpaper Color": "انتخاب رنگ تصویر پس‌زمینه"
},
"wallpaper cycling mode tab": {
"Interval": "وقفه"
},
"wallpaper directory file browser title": {
"Select Wallpaper Directory": "انتخاب دایرکتوری تصاویر پس‌زمینه"
},
@@ -6330,34 +6171,6 @@
"Tile H": "کاشی افقی",
"Tile V": "کاشی عمودی"
},
"wallpaper interval": {
"1 hour": "۱ ساعت",
"1 hour 30 minutes": "۱ ساعت و نیم",
"1 minute": "۱ دقیقه",
"10 seconds": "۱۰ ثانیه",
"12 hours": "۱۲ ساعت",
"15 minutes": "۱۵ دقیقه",
"15 seconds": "۱۵ ثانیه",
"2 hours": "۲ ساعت",
"20 seconds": "۲۰ ثانیه",
"25 seconds": "۲۵ ثانیه",
"3 hours": "۳ ساعت",
"30 minutes": "۳۰ دقیقه",
"30 seconds": "۳۰ ثانیه",
"35 seconds": "۳۵ ثانیه",
"4 hours": "۴ ساعت",
"40 seconds": "۴۰ ثانیه",
"45 seconds": "۴۵ ثانیه",
"5 minutes": "۵ دقیقه",
"5 seconds": "۵ ثانیه",
"50 seconds": "۵۰ ثانیه",
"55 seconds": "۵۵ ثانیه",
"6 hours": "۶ ساعت",
"8 hours": "۸ ساعت"
},
"wallpaper not set label": {
"Not set": "تنظیم نشده"
},
"wallpaper processing error": {
"Wallpaper processing failed": "پردازش تصویر پس‌زمینه ناموفق بود"
},
@@ -6432,8 +6245,5 @@
},
"• yyyy - Year (2024)": {
"• yyyy - Year (2024)": "• yyyy - سال (2024)"
},
"↑/↓: Nav • Space: Expand • Enter: Action/Expand • E: Text": {
"↑/↓: Nav • Space: Expand • Enter: Action/Expand • E: Text": ""
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More