1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00

Compare commits

...

18 Commits

Author SHA1 Message Date
dms-ci[bot]
6297b0679c nix: update vendorHash for go.mod changes 2025-11-30 06:43:50 +00:00
bbedward
d62ef635a7 ci: use gh app 2025-11-30 01:42:15 -05:00
bbedward
c53836040f dankbar: add width/height deps to binding 2025-11-30 01:28:04 -05:00
bbedward
0b638bf85f ci: add update-vendor trigger 2025-11-30 01:23:23 -05:00
bbedward
7f6a71b964 ci: switch to gh pat 2025-11-30 01:20:19 -05:00
bbedward
1b4363a54a dankbar: dont early return in path functions 2025-11-30 01:08:38 -05:00
bbedward
16d168c970 core: update deps 2025-11-30 01:05:15 -05:00
bbedward
4606d7960e dankbar: remove caching redraw prevention 2025-11-30 00:56:36 -05:00
bbedward
4eee126d26 media: suppress media OSD on new players for 2s
fixes #838
2025-11-30 00:35:24 -05:00
bbedward
dde426658f core: fix golang-ci lints and add a config 2025-11-30 00:12:45 -05:00
bbedward
f6874fbcad workflow: run go CI on PRs 2025-11-29 23:35:40 -05:00
bbedward
621d4e4d92 dankbar: remove barTint Shape 2025-11-29 23:12:12 -05:00
bbedward
76062231fd dankbar: another hack to try and fix opacity 2025-11-29 23:06:49 -05:00
bbedward
261f55fea5 dankbar: simplify transparency binding 2025-11-29 22:55:14 -05:00
bbedward
202cf4bcc9 dankbar: try something else for binding 2025-11-29 22:43:55 -05:00
Willem Schipper
b7572f727f feat: allow popout to resize to its contents (#847) 2025-11-29 22:39:30 -05:00
bbedward
50ab346d58 dankbar: try to fix binding issues on creation 2025-11-29 22:36:20 -05:00
bbedward
b11b375848 settings: optimize mem usage
- keep un-loaded unless called upon
2025-11-29 18:32:45 -05:00
36 changed files with 703 additions and 603 deletions

View File

@@ -7,9 +7,18 @@ on:
paths: paths:
- 'core/**' - 'core/**'
- '.github/workflows/go-ci.yml' - '.github/workflows/go-ci.yml'
pull_request:
branches: [master, main]
paths:
- 'core/**'
- '.github/workflows/go-ci.yml'
concurrency:
group: go-ci-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
test: lint-and-test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
defaults: defaults:
run: run:
@@ -32,11 +41,20 @@ jobs:
exit 1 exit 1
fi fi
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest
working-directory: core
- name: Test - name: Test
run: go test -v ./... run: go test -v ./...
- name: Build dms - name: Build dms
run: go build -v ./cmd/dms run: go build -v ./cmd/dms
- name: Build dms (distropkg)
run: go build -v -tags distro_binary ./cmd/dms
- name: Build dankinstall - name: Build dankinstall
run: go build -v ./cmd/dankinstall run: go build -v ./cmd/dankinstall

View File

@@ -132,38 +132,40 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build-core needs: build-core
steps: steps:
- name: Create GitHub App token
id: app_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ steps.app_token.outputs.token }}
fetch-depth: 0 fetch-depth: 0
- name: Update VERSION - name: Update VERSION
env:
GH_TOKEN: ${{ steps.app_token.outputs.token }}
run: | run: |
set -euo pipefail set -euo pipefail
git config user.name "github-actions[bot]" git config user.name "dms-ci[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com" git config user.email "dms-ci[bot]@users.noreply.github.com"
version="${GITHUB_REF#refs/tags/}" version="${GITHUB_REF#refs/tags/}"
version_no_v="${version#v}"
echo "Updating to version: $version" echo "Updating to version: $version"
# Update VERSION file in quickshell/
echo "${version}" > quickshell/VERSION echo "${version}" > quickshell/VERSION
git add quickshell/VERSION git add quickshell/VERSION
if ! git diff --cached --quiet; then if ! git diff --cached --quiet; then
git commit -m "chore: bump version to $version" git commit -m "chore: bump version to $version"
git push origin HEAD:master || git push origin HEAD:main git pull --rebase origin master
echo "Pushed version updates to master" git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git HEAD:master
else
echo "No version changes needed"
fi fi
# Force-push the tag to point to the commit with updated VERSION
git tag -f "${version}" git tag -f "${version}"
git push -f origin "${version}" git push -f https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git "${version}"
release: release:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04

View File

@@ -1,6 +1,7 @@
name: Update Vendor Hash name: Update Vendor Hash
on: on:
workflow_dispatch:
push: push:
paths: paths:
- "core/go.mod" - "core/go.mod"
@@ -8,14 +9,25 @@ on:
branches: branches:
- master - master
permissions:
contents: write
jobs: jobs:
update-vendor-hash: update-vendor-hash:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Create GitHub App token
id: app_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ steps.app_token.outputs.token }}
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v31 uses: cachix/install-nix-action@v31
@@ -23,68 +35,32 @@ jobs:
- name: Update vendorHash in flake.nix - name: Update vendorHash in flake.nix
run: | run: |
set -euo pipefail set -euo pipefail
# Try to build and capture the expected hash from error message
echo "Attempting nix build to get new vendorHash..." echo "Attempting nix build to get new vendorHash..."
if output=$(nix build .#dmsCli 2>&1); then if output=$(nix build .#dmsCli 2>&1); then
echo "Build succeeded, no hash update needed" echo "Build succeeded, no hash update needed"
exit 0 exit 0
fi fi
# Extract the expected hash from the error message
new_hash=$(echo "$output" | grep -oP "got:\s+\K\S+" | head -n1) new_hash=$(echo "$output" | grep -oP "got:\s+\K\S+" | head -n1)
[ -n "$new_hash" ] || { echo "Could not extract new vendorHash"; echo "$output"; exit 1; }
if [ -z "$new_hash" ]; then
echo "Could not extract new vendorHash from build output"
echo "Build output:"
echo "$output"
exit 1
fi
echo "New vendorHash: $new_hash"
# Get current hash from flake.nix
current_hash=$(grep -oP 'vendorHash = "\K[^"]+' flake.nix) current_hash=$(grep -oP 'vendorHash = "\K[^"]+' flake.nix)
echo "Current vendorHash: $current_hash" [ "$current_hash" = "$new_hash" ] && { echo "vendorHash already up to date"; exit 0; }
if [ "$current_hash" = "$new_hash" ]; then
echo "vendorHash is already up to date"
exit 0
fi
# Update the hash in flake.nix
sed -i "s|vendorHash = \"$current_hash\"|vendorHash = \"$new_hash\"|" flake.nix sed -i "s|vendorHash = \"$current_hash\"|vendorHash = \"$new_hash\"|" flake.nix
# Verify the build works with the new hash
echo "Verifying build with new vendorHash..." echo "Verifying build with new vendorHash..."
nix build .#dmsCli nix build .#dmsCli
echo "vendorHash updated successfully!" echo "vendorHash updated successfully!"
- name: Commit and push vendorHash update - name: Commit and push vendorHash update
env:
GH_TOKEN: ${{ steps.app_token.outputs.token }}
run: | run: |
set -euo pipefail set -euo pipefail
if ! git diff --quiet flake.nix; then if ! git diff --quiet flake.nix; then
git config user.name "github-actions[bot]" git config user.name "dms-ci[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com" git config user.email "dms-ci[bot]@users.noreply.github.com"
git add flake.nix git add flake.nix
git commit -m "nix: update vendorHash for go.mod changes" git commit -m "nix: update vendorHash for go.mod changes" || exit 0
git pull --rebase origin master
for attempt in 1 2 3; do git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git HEAD:master
if git push; then
echo "Successfully pushed vendorHash update"
exit 0
fi
echo "Push attempt $attempt failed, pulling and retrying..."
git pull --rebase
sleep $((attempt*2))
done
echo "Failed to push after retries" >&2
exit 1
else else
echo "No changes to flake.nix" echo "No changes to flake.nix"
fi fi

77
core/.golangci.yml Normal file
View File

@@ -0,0 +1,77 @@
linters-settings:
errcheck:
check-type-assertions: false
check-blank: false
exclude-functions:
# Cleanup/destroy operations
- (io.Closer).Close
- (*os.File).Close
- (net.Conn).Close
- (*net.Conn).Close
# Signal handling
- (*os.Process).Signal
- (*os.Process).Kill
# DBus cleanup
- (*github.com/godbus/dbus/v5.Conn).RemoveMatchSignal
- (*github.com/godbus/dbus/v5.Conn).RemoveSignal
# Encoding to network connections (if conn is bad, nothing we can do)
- (*encoding/json.Encoder).Encode
- (net.Conn).Write
# Command execution where failure is expected/ignored
- (*os/exec.Cmd).Run
- (*os/exec.Cmd).Start
# Flush operations
- (*bufio.Writer).Flush
# Scanning user input
- fmt.Scanln
- fmt.Scanf
# Parse operations where default value is acceptable
- fmt.Sscanf
# Flag operations
- (*github.com/spf13/pflag.FlagSet).MarkHidden
# Binary encoding to buffer (can't fail for basic types)
- binary.Write
# File operations in cleanup paths
- os.Rename
- os.Remove
- (*os.File).WriteString
issues:
exclude-rules:
- path: _test\.go
linters:
- errcheck
- govet
- unused
- ineffassign
- staticcheck
- gosimple
# Exclude cleanup/teardown method calls from errcheck
- linters:
- errcheck
text: "Error return value of `.+\\.(Destroy|Release|Stop|Close|Roundtrip|Store)` is not checked"
# Exclude internal state update methods that are best-effort
- linters:
- errcheck
text: "Error return value of `[mb]\\.\\w*(update|initialize|recreate|acquire|enumerate|list|List|Ensure|refresh|Lock)\\w*` is not checked"
# Exclude SetMode on wayland power controls (best-effort)
- linters:
- errcheck
text: "Error return value of `.+\\.SetMode` is not checked"
# Exclude AddMatchSignal which is best-effort monitoring setup
- linters:
- errcheck
text: "Error return value of `.+\\.AddMatchSignal` is not checked"
# Exclude wayland pkg from errcheck and ineffassign (generated code patterns)
- linters:
- errcheck
- ineffassign
path: pkg/go-wayland/
# Exclude proto pkg from ineffassign (generated protocol code)
- linters:
- ineffassign
path: internal/proto/
# binary.Write to bytes.Buffer can't fail
- linters:
- errcheck
text: "Error return value of `binary\\.Write` is not checked"

View File

@@ -11,6 +11,8 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/greeter" "github.com/AvengeMedia/DankMaterialShell/core/internal/greeter"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log" "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
) )
var greeterCmd = &cobra.Command{ var greeterCmd = &cobra.Command{
@@ -258,7 +260,7 @@ func disableDisplayManager(dmName string) (bool, error) {
} else if shouldDisable { } else if shouldDisable {
return actionTaken, fmt.Errorf("%s is still in state '%s' after %s operation", dmName, enabledState, actionVerb) return actionTaken, fmt.Errorf("%s is still in state '%s' after %s operation", dmName, enabledState, actionVerb)
} else { } else {
fmt.Printf(" ✓ %s %s (now: %s)\n", strings.Title(actionVerb), dmName, enabledState) fmt.Printf(" ✓ %s %s (now: %s)\n", cases.Title(language.English).String(actionVerb), dmName, enabledState)
} }
actionTaken = true actionTaken = true

View File

@@ -9,24 +9,24 @@ require (
github.com/charmbracelet/lipgloss v1.1.0 github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/log v0.4.2 github.com/charmbracelet/log v0.4.2
github.com/fsnotify/fsnotify v1.9.0 github.com/fsnotify/fsnotify v1.9.0
github.com/godbus/dbus/v5 v5.1.0 github.com/godbus/dbus/v5 v5.2.0
github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83 github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83
github.com/spf13/cobra v1.10.1 github.com/spf13/cobra v1.10.1
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.11.1
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39
) )
require ( require (
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/clipperhouse/displaywidth v0.5.0 // indirect github.com/clipperhouse/displaywidth v0.6.0 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect github.com/cloudflare/circl v1.6.1 // indirect
github.com/cyphar/filepath-securejoin v0.6.0 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg/v2 v2.0.2 // indirect github.com/go-git/gcfg/v2 v2.0.2 // indirect
github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0 // indirect github.com/go-git/go-billy/v6 v6.0.0-20251126203821-7f9c95185ee0 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect github.com/kevinburke/ssh_config v1.4.0 // indirect
@@ -34,7 +34,7 @@ require (
github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect
github.com/sergi/go-diff v1.4.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect
github.com/stretchr/objx v0.5.3 // indirect github.com/stretchr/objx v0.5.3 // indirect
golang.org/x/crypto v0.44.0 // indirect golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect golang.org/x/net v0.47.0 // indirect
) )
@@ -43,12 +43,12 @@ require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.3.3 // indirect github.com/charmbracelet/colorprofile v0.3.3 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/charmbracelet/x/ansi v0.11.0 // indirect github.com/charmbracelet/x/ansi v0.11.2 // indirect
github.com/charmbracelet/x/cellbuf v0.0.14 // indirect github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9 github.com/go-git/go-git/v6 v6.0.0-20251128074608-48f817f57805
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 github.com/lucasb-eyer/go-colorful v1.3.0
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
@@ -63,6 +63,6 @@ require (
github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/pflag v1.0.10 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.38.0 golang.org/x/sys v0.38.0
golang.org/x/text v0.31.0 // indirect golang.org/x/text v0.31.0
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View File

@@ -26,12 +26,16 @@ github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsy
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
github.com/charmbracelet/x/ansi v0.11.0 h1:uuIVK7GIplwX6UBIz8S2TF8nkr7xRlygSsBRjSJqIvA= github.com/charmbracelet/x/ansi v0.11.0 h1:uuIVK7GIplwX6UBIz8S2TF8nkr7xRlygSsBRjSJqIvA=
github.com/charmbracelet/x/ansi v0.11.0/go.mod h1:uQt8bOrq/xgXjlGcFMc8U2WYbnxyjrKhnvTQluvfCaE= github.com/charmbracelet/x/ansi v0.11.0/go.mod h1:uQt8bOrq/xgXjlGcFMc8U2WYbnxyjrKhnvTQluvfCaE=
github.com/charmbracelet/x/ansi v0.11.2 h1:XAG3FSjiVtFvgEgGrNBkCNNYrsucAt8c6bfxHyROLLs=
github.com/charmbracelet/x/ansi v0.11.2/go.mod h1:9tY2bzX5SiJCU0iWyskjBeI2BRQfvPqI+J760Mjf+Rg=
github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4= github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4=
github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA= github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/clipperhouse/displaywidth v0.5.0 h1:AIG5vQaSL2EKqzt0M9JMnvNxOCRTKUc4vUnLWGgP89I= github.com/clipperhouse/displaywidth v0.5.0 h1:AIG5vQaSL2EKqzt0M9JMnvNxOCRTKUc4vUnLWGgP89I=
github.com/clipperhouse/displaywidth v0.5.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o= github.com/clipperhouse/displaywidth v0.5.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/displaywidth v0.6.0 h1:k32vueaksef9WIKCNcoqRNyKbyvkvkysNYnAWz2fN4s=
github.com/clipperhouse/displaywidth v0.6.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
@@ -41,6 +45,8 @@ github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZ
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is= github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -58,14 +64,20 @@ github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs= github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs=
github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0 h1:EC9n6hr6yKDoVJ6g7Ko523LbbceJfR0ohbOp809Fyf4= github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0 h1:EC9n6hr6yKDoVJ6g7Ko523LbbceJfR0ohbOp809Fyf4=
github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0/go.mod h1:E3VhlS+AKkrq6ZNn1axE2/nDRJ87l1FJk9r5HT2vPX0= github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0/go.mod h1:E3VhlS+AKkrq6ZNn1axE2/nDRJ87l1FJk9r5HT2vPX0=
github.com/go-git/go-billy/v6 v6.0.0-20251126203821-7f9c95185ee0 h1:eY5aB2GXiVdgTueBcqsBt53WuJTRZAuCdIS/86Pcq5c=
github.com/go-git/go-billy/v6 v6.0.0-20251126203821-7f9c95185ee0/go.mod h1:0NjwVNrwtVFZBReAp5OoGklGJIgJFEbVyHneAr4lc8k=
github.com/go-git/go-git-fixtures/v5 v5.1.1 h1:OH8i1ojV9bWfr0ZfasfpgtUXQHQyVS8HXik/V1C099w= github.com/go-git/go-git-fixtures/v5 v5.1.1 h1:OH8i1ojV9bWfr0ZfasfpgtUXQHQyVS8HXik/V1C099w=
github.com/go-git/go-git-fixtures/v5 v5.1.1/go.mod h1:Altk43lx3b1ks+dVoAG2300o5WWUnktvfY3VI6bcaXU= github.com/go-git/go-git-fixtures/v5 v5.1.1/go.mod h1:Altk43lx3b1ks+dVoAG2300o5WWUnktvfY3VI6bcaXU=
github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9 h1:SOFrnF9LCssC6q6Rb0084Bzg2aBYbe8QXv9xKGXmt/w= github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9 h1:SOFrnF9LCssC6q6Rb0084Bzg2aBYbe8QXv9xKGXmt/w=
github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9/go.mod h1:0wtvm/JfPC9RFVEAP3ks0ec5h64/qmZkTTUE3pjz7Hc= github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9/go.mod h1:0wtvm/JfPC9RFVEAP3ks0ec5h64/qmZkTTUE3pjz7Hc=
github.com/go-git/go-git/v6 v6.0.0-20251128074608-48f817f57805 h1:jxQ3BzYeErNRvlI/4+0mpwqMzvB4g97U+ksfgvrUEbY=
github.com/go-git/go-git/v6 v6.0.0-20251128074608-48f817f57805/go.mod h1:dIwT3uWK1ooHInyVnK2JS5VfQ3peVGYaw2QPqX7uFvs=
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE= github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk= github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8=
github.com/godbus/dbus/v5 v5.2.0/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -127,8 +139,12 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0=
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY=
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@@ -367,7 +367,7 @@ func SyncDMSConfigs(dmsPath string, logFunc func(string), sudoPassword string) e
} }
} }
runSudoCmd(sudoPassword, "rm", "-f", link.target) runSudoCmd(sudoPassword, "rm", "-f", link.target) //nolint:errcheck
if err := runSudoCmd(sudoPassword, "ln", "-sf", link.source, link.target); err != nil { if err := runSudoCmd(sudoPassword, "ln", "-sf", link.source, link.target); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: Failed to create symlink for %s: %v", link.desc, err)) logFunc(fmt.Sprintf("⚠ Warning: Failed to create symlink for %s: %v", link.desc, err))

View File

@@ -77,7 +77,7 @@ func (d *DiscoveryConfig) FindJSONFiles() ([]string, error) {
func expandPath(path string) (string, error) { func expandPath(path string) (string, error) {
expandedPath := os.ExpandEnv(path) expandedPath := os.ExpandEnv(path)
if filepath.HasPrefix(expandedPath, "~") { if strings.HasPrefix(expandedPath, "~") {
home, err := os.UserHomeDir() home, err := os.UserHomeDir()
if err != nil { if err != nil {
return "", err return "", err

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds" "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
) )
@@ -118,7 +119,7 @@ func (j *JSONFileProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
func expandPath(path string) (string, error) { func expandPath(path string) (string, error) {
expandedPath := os.ExpandEnv(path) expandedPath := os.ExpandEnv(path)
if filepath.HasPrefix(expandedPath, "~") { if strings.HasPrefix(expandedPath, "~") {
home, err := os.UserHomeDir() home, err := os.UserHomeDir()
if err != nil { if err != nil {
return "", err return "", err

View File

@@ -64,7 +64,7 @@ func (l *FileLogger) writeToFile(message string) {
redacted := l.redactPassword(message) redacted := l.redactPassword(message)
timestamp := time.Now().Format("15:04:05.000") timestamp := time.Now().Format("15:04:05.000")
l.writer.WriteString(fmt.Sprintf("[%s] %s\n", timestamp, redacted)) l.writer.WriteString(fmt.Sprintf("[%s] %s\n", timestamp, redacted)) //nolint:errcheck
l.writer.Flush() l.writer.Flush()
} }
@@ -93,7 +93,7 @@ func (l *FileLogger) Close() error {
defer l.mu.Unlock() defer l.mu.Unlock()
footer := fmt.Sprintf("\n=== DankInstall Log End ===\nCompleted: %s\n", time.Now().Format(time.RFC3339)) footer := fmt.Sprintf("\n=== DankInstall Log End ===\nCompleted: %s\n", time.Now().Format(time.RFC3339))
l.writer.WriteString(footer) l.writer.WriteString(footer) //nolint:errcheck
l.writer.Flush() l.writer.Flush()
if err := l.file.Sync(); err != nil { if err := l.file.Sync(); err != nil {

View File

@@ -93,7 +93,7 @@ func (m *Manager) Install(plugin Plugin) error {
if !repoExists { if !repoExists {
if err := m.gitClient.PlainClone(repoPath, plugin.Repo); err != nil { if err := m.gitClient.PlainClone(repoPath, plugin.Repo); err != nil {
m.fs.RemoveAll(repoPath) m.fs.RemoveAll(repoPath) //nolint:errcheck
return fmt.Errorf("failed to clone repository: %w", err) return fmt.Errorf("failed to clone repository: %w", err)
} }
} else { } else {
@@ -130,7 +130,7 @@ func (m *Manager) Install(plugin Plugin) error {
} }
} else { } else {
if err := m.gitClient.PlainClone(pluginPath, plugin.Repo); err != nil { if err := m.gitClient.PlainClone(pluginPath, plugin.Repo); err != nil {
m.fs.RemoveAll(pluginPath) m.fs.RemoveAll(pluginPath) //nolint:errcheck
return fmt.Errorf("failed to clone plugin: %w", err) return fmt.Errorf("failed to clone plugin: %w", err)
} }
} }

View File

@@ -98,7 +98,7 @@ func (b *DDCBackend) probeDDCDevice(bus int) (*ddcDevice, error) {
} }
dummy := make([]byte, 32) dummy := make([]byte, 32)
syscall.Read(fd, dummy) syscall.Read(fd, dummy) //nolint:errcheck
writebuf := []byte{0x00} writebuf := []byte{0x00}
n, err := syscall.Write(fd, writebuf) n, err := syscall.Write(fd, writebuf)

View File

@@ -13,8 +13,7 @@ type DBusConn interface {
} }
type LogindBackend struct { type LogindBackend struct {
conn DBusConn conn DBusConn
connOnce bool
} }
func NewLogindBackend() (*LogindBackend, error) { func NewLogindBackend() (*LogindBackend, error) {

View File

@@ -251,14 +251,14 @@ func (m *Manager) CreatePrinter(name, deviceURI, ppd string, shared bool, errorP
} }
if usedPkHelper { if usedPkHelper {
m.pkHelper.PrinterSetEnabled(name, true) m.pkHelper.PrinterSetEnabled(name, true) //nolint:errcheck
m.pkHelper.PrinterSetAcceptJobs(name, true, "") m.pkHelper.PrinterSetAcceptJobs(name, true, "") //nolint:errcheck
} else { } else {
if err := m.client.ResumePrinter(name); isAuthError(err) && m.pkHelper != nil { if err := m.client.ResumePrinter(name); isAuthError(err) && m.pkHelper != nil {
m.pkHelper.PrinterSetEnabled(name, true) m.pkHelper.PrinterSetEnabled(name, true) //nolint:errcheck
} }
if err := m.client.AcceptJobs(name); isAuthError(err) && m.pkHelper != nil { if err := m.client.AcceptJobs(name); isAuthError(err) && m.pkHelper != nil {
m.pkHelper.PrinterSetAcceptJobs(name, true, "") m.pkHelper.PrinterSetAcceptJobs(name, true, "") //nolint:errcheck
} }
} }

View File

@@ -11,6 +11,8 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log" "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5"
"golang.org/x/text/cases"
"golang.org/x/text/language"
) )
const ( const (
@@ -118,12 +120,6 @@ func (a *SecretAgent) GetSecrets(
log.Infof("[SecretAgent] GetSecrets called: path=%s, setting=%s, hints=%v, flags=%d", log.Infof("[SecretAgent] GetSecrets called: path=%s, setting=%s, hints=%v, flags=%d",
path, settingName, hints, flags) path, settingName, hints, flags)
const (
NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 0x1
NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW = 0x2
NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED = 0x4
)
connType, displayName, vpnSvc := readConnTypeAndName(conn) connType, displayName, vpnSvc := readConnTypeAndName(conn)
ssid := readSSID(conn) ssid := readSSID(conn)
fields := fieldsNeeded(settingName, hints) fields := fieldsNeeded(settingName, hints)
@@ -378,7 +374,7 @@ func (a *SecretAgent) GetSecrets(
} }
if settingName == "vpn" && a.backend != nil && (vpnUsername != "" || reply.Save) { if settingName == "vpn" && a.backend != nil && (vpnUsername != "" || reply.Save) {
pw, _ := reply.Secrets["password"] pw := reply.Secrets["password"]
a.backend.pendingVPNSaveMu.Lock() a.backend.pendingVPNSaveMu.Lock()
a.backend.pendingVPNSave = &pendingVPNCredentials{ a.backend.pendingVPNSave = &pendingVPNCredentials{
ConnectionPath: string(path), ConnectionPath: string(path),
@@ -620,11 +616,12 @@ func vpnFieldMeta(field, vpnService string) (label string, isSecret bool) {
case "private-key-password": case "private-key-password":
return "Private Key Password", true return "Private Key Password", true
} }
titleCaser := cases.Title(language.English)
if strings.HasSuffix(field, "password") || strings.HasSuffix(field, "secret") || if strings.HasSuffix(field, "password") || strings.HasSuffix(field, "secret") ||
strings.HasSuffix(field, "pass") || strings.HasSuffix(field, "psk") { strings.HasSuffix(field, "pass") || strings.HasSuffix(field, "psk") {
return strings.Title(strings.ReplaceAll(field, "-", " ")), true return titleCaser.String(strings.ReplaceAll(field, "-", " ")), true
} }
return strings.Title(strings.ReplaceAll(field, "-", " ")), false return titleCaser.String(strings.ReplaceAll(field, "-", " ")), false
} }
func readVPNPasswordFlags(conn map[string]nmVariantMap, settingName string) uint32 { func readVPNPasswordFlags(conn map[string]nmVariantMap, settingName string) uint32 {

View File

@@ -2,14 +2,12 @@ package network
import ( import (
"fmt" "fmt"
"sync"
) )
type HybridIwdNetworkdBackend struct { type HybridIwdNetworkdBackend struct {
wifi *IWDBackend wifi *IWDBackend
l3 *SystemdNetworkdBackend l3 *SystemdNetworkdBackend
onStateChange func() onStateChange func()
stateMutex sync.RWMutex
} }
func NewHybridIwdNetworkdBackend(w *IWDBackend, n *SystemdNetworkdBackend) (*HybridIwdNetworkdBackend, error) { func NewHybridIwdNetworkdBackend(w *IWDBackend, n *SystemdNetworkdBackend) (*HybridIwdNetworkdBackend, error) {
@@ -120,7 +118,7 @@ func (b *HybridIwdNetworkdBackend) ConnectWiFi(req ConnectionRequest) error {
ws, err := b.wifi.GetCurrentState() ws, err := b.wifi.GetCurrentState()
if err == nil && ws.WiFiDevice != "" { if err == nil && ws.WiFiDevice != "" {
b.l3.EnsureDhcpUp(ws.WiFiDevice) b.l3.EnsureDhcpUp(ws.WiFiDevice) //nolint:errcheck
} }
return nil return nil

View File

@@ -5,6 +5,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5"
) )
@@ -159,6 +160,7 @@ func (b *IWDBackend) OnUserCanceledPrompt() {
if cancelledSSID != "" { if cancelledSSID != "" {
if err := b.ForgetWiFiNetwork(cancelledSSID); err != nil { if err := b.ForgetWiFiNetwork(cancelledSSID); err != nil {
log.Warnf("failed to forget cancelled WiFi network %s: %v", cancelledSSID, err)
} }
} }

View File

@@ -319,7 +319,6 @@ func handleConnection(conn net.Conn) {
capsData, _ := json.Marshal(caps) capsData, _ := json.Marshal(caps)
conn.Write(capsData) conn.Write(capsData)
conn.Write([]byte("\n")) conn.Write([]byte("\n"))
scanner := bufio.NewScanner(conn) scanner := bufio.NewScanner(conn)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Bytes() line := scanner.Bytes()

View File

@@ -72,10 +72,10 @@ func (m Model) viewSelectTerminal() string {
b.WriteString(title) b.WriteString(title)
b.WriteString("\n\n") b.WriteString("\n\n")
options := []struct { var options []struct {
name string name string
description string description string
}{} }
if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" {
options = []struct { options = []struct {

View File

@@ -7,6 +7,8 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
) )
type VersionInfo struct { type VersionInfo struct {
@@ -33,7 +35,11 @@ func (d *DefaultVersionFetcher) GetCurrentVersion(dmsPath string) (string, error
if err != nil { if err != nil {
return "", err return "", err
} }
defer os.Chdir(originalDir) defer func() {
if err := os.Chdir(originalDir); err != nil {
log.Warnf("failed to change back to original directory: %v", err)
}
}()
if err := os.Chdir(dmsPath); err != nil { if err := os.Chdir(dmsPath); err != nil {
return "", fmt.Errorf("failed to change to DMS directory: %w", err) return "", fmt.Errorf("failed to change to DMS directory: %w", err)
@@ -70,7 +76,11 @@ func (d *DefaultVersionFetcher) GetLatestVersion(dmsPath string) (string, error)
if err != nil { if err != nil {
return "", err return "", err
} }
defer os.Chdir(originalDir) defer func() {
if err := os.Chdir(originalDir); err != nil {
log.Warnf("failed to change back to original directory: %v", err)
}
}()
if err := os.Chdir(dmsPath); err != nil { if err := os.Chdir(dmsPath); err != nil {
return "", fmt.Errorf("failed to change to DMS directory: %w", err) return "", fmt.Errorf("failed to change to DMS directory: %w", err)
@@ -85,7 +95,9 @@ func (d *DefaultVersionFetcher) GetLatestVersion(dmsPath string) (string, error)
if _, err := tagCmd.Output(); err == nil { if _, err := tagCmd.Output(); err == nil {
// Add timeout to git fetch to prevent hanging // Add timeout to git fetch to prevent hanging
fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", "--tags", "--quiet") fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", "--tags", "--quiet")
fetchCmd.Run() if err := fetchCmd.Run(); err != nil {
log.Debugf("git fetch tags failed (continuing with local tags): %v", err)
}
latestTagCmd := exec.Command("git", "tag", "-l", "v*", "--sort=-version:refname") latestTagCmd := exec.Command("git", "tag", "-l", "v*", "--sort=-version:refname")
latestTagOutput, err := latestTagCmd.Output() latestTagOutput, err := latestTagCmd.Output()
@@ -109,7 +121,9 @@ func (d *DefaultVersionFetcher) GetLatestVersion(dmsPath string) (string, error)
// Add timeout to git fetch to prevent hanging // Add timeout to git fetch to prevent hanging
fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", currentBranch, "--quiet") fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", currentBranch, "--quiet")
fetchCmd.Run() if err := fetchCmd.Run(); err != nil {
log.Debugf("git fetch branch failed (continuing with local ref): %v", err)
}
remoteRevCmd := exec.Command("git", "rev-parse", "--short", fmt.Sprintf("origin/%s", currentBranch)) remoteRevCmd := exec.Command("git", "rev-parse", "--short", fmt.Sprintf("origin/%s", currentBranch))
remoteRevOutput, err := remoteRevCmd.Output() remoteRevOutput, err := remoteRevCmd.Output()
@@ -236,10 +250,10 @@ func CompareVersions(v1, v2 string) int {
for i := 0; i < maxLen; i++ { for i := 0; i < maxLen; i++ {
var p1, p2 int var p1, p2 int
if i < len(parts1) { if i < len(parts1) {
fmt.Sscanf(parts1[i], "%d", &p1) fmt.Sscanf(parts1[i], "%d", &p1) //nolint:errcheck
} }
if i < len(parts2) { if i < len(parts2) {
fmt.Sscanf(parts2[i], "%d", &p2) fmt.Sscanf(parts2[i], "%d", &p2) //nolint:errcheck
} }
if p1 < p2 { if p1 < p2 {

View File

@@ -82,7 +82,6 @@ func (i *Display) Sync() (*Callback, error) {
PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
l += 4 l += 4
PutUint32(_reqBuf[l:l+4], callback.ID()) PutUint32(_reqBuf[l:l+4], callback.ID())
l += 4
err := i.Context().WriteMsg(_reqBuf[:], nil) err := i.Context().WriteMsg(_reqBuf[:], nil)
return callback, err return callback, err
} }
@@ -109,7 +108,6 @@ func (i *Display) GetRegistry() (*Registry, error) {
PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff))
l += 4 l += 4
PutUint32(_reqBuf[l:l+4], registry.ID()) PutUint32(_reqBuf[l:l+4], registry.ID())
l += 4
err := i.Context().WriteMsg(_reqBuf[:], nil) err := i.Context().WriteMsg(_reqBuf[:], nil)
return registry, err return registry, err
} }
@@ -223,7 +221,6 @@ func (i *Display) Dispatch(opcode uint32, fd int, data []byte) {
messageLen := PaddedLen(int(Uint32(data[l : l+4]))) messageLen := PaddedLen(int(Uint32(data[l : l+4])))
l += 4 l += 4
e.Message = String(data[l : l+messageLen]) e.Message = String(data[l : l+messageLen])
l += messageLen
i.errorHandler(e) i.errorHandler(e)
case 1: case 1:

View File

@@ -51,7 +51,7 @@
pname = "dmsCli"; pname = "dmsCli";
src = ./core; src = ./core;
vendorHash = "sha256-nc4CvEPfJ6l16/zmhnXr1jqpi6BeSXd3g/51djbEfpQ="; vendorHash = "sha256-XBPJVgncI98VFch3wwkq1/m5Jm7DrV4oGS2p1bPJRag=";
subPackages = ["cmd/dms"]; subPackages = ["cmd/dms"];

View File

@@ -108,10 +108,13 @@ Item {
id: barRepeaterModel id: barRepeaterModel
values: { values: {
const configs = SettingsData.barConfigs; const configs = SettingsData.barConfigs;
return configs.map(c => ({ return configs
id: c.id, .map(c => ({ id: c.id, position: c.position }))
position: c.position .sort((a, b) => {
})); const aVertical = a.position === SettingsData.Position.Left || a.position === SettingsData.Position.Right;
const bVertical = b.position === SettingsData.Position.Left || b.position === SettingsData.Position.Right;
return aVertical - bVertical;
});
} }
} }
@@ -120,7 +123,7 @@ Item {
delegate: Loader { delegate: Loader {
id: barLoader id: barLoader
required property var modelData required property var modelData
property var barConfig: SettingsData.getBarConfig(modelData.id) property var barConfig: SettingsData.barConfigs.find(cfg => cfg.id === modelData.id) || null
active: barConfig?.enabled ?? false active: barConfig?.enabled ?? false
asynchronous: false asynchronous: false
@@ -360,11 +363,33 @@ Item {
} }
} }
SettingsModal { LazyLoader {
id: settingsModal id: settingsModalLoader
active: false
Component.onCompleted: { Component.onCompleted: {
PopoutService.settingsModal = settingsModal; PopoutService.settingsModalLoader = settingsModalLoader;
}
onActiveChanged: {
if (active && item) {
PopoutService.settingsModal = item;
PopoutService._onSettingsModalLoaded();
}
}
SettingsModal {
id: settingsModal
property bool wasShown: false
onVisibleChanged: {
if (visible) {
wasShown = true;
} else if (wasShown) {
PopoutService.unloadSettings();
}
}
} }
} }
@@ -534,7 +559,6 @@ Item {
hyprKeybindsModalLoader: hyprKeybindsModalLoader hyprKeybindsModalLoader: hyprKeybindsModalLoader
dankBarRepeater: dankBarRepeater dankBarRepeater: dankBarRepeater
hyprlandOverviewLoader: hyprlandOverviewLoader hyprlandOverviewLoader: hyprlandOverviewLoader
settingsModal: settingsModal
} }
Variants { Variants {

View File

@@ -15,7 +15,6 @@ Item {
required property var hyprKeybindsModalLoader required property var hyprKeybindsModalLoader
required property var dankBarRepeater required property var dankBarRepeater
required property var hyprlandOverviewLoader required property var hyprlandOverviewLoader
required property var settingsModal
function getFirstBar() { function getFirstBar() {
if (!root.dankBarRepeater || root.dankBarRepeater.count === 0) if (!root.dankBarRepeater || root.dankBarRepeater.count === 0)
@@ -533,17 +532,17 @@ Item {
IpcHandler { IpcHandler {
function open(): string { function open(): string {
root.settingsModal.show(); PopoutService.openSettings();
return "SETTINGS_OPEN_SUCCESS"; return "SETTINGS_OPEN_SUCCESS";
} }
function close(): string { function close(): string {
root.settingsModal.hide(); PopoutService.closeSettings();
return "SETTINGS_CLOSE_SUCCESS"; return "SETTINGS_CLOSE_SUCCESS";
} }
function toggle(): string { function toggle(): string {
root.settingsModal.toggle(); PopoutService.toggleSettings();
return "SETTINGS_TOGGLE_SUCCESS"; return "SETTINGS_TOGGLE_SUCCESS";
} }
@@ -552,12 +551,17 @@ Item {
IpcHandler { IpcHandler {
function browse(type: string) { function browse(type: string) {
if (type === "wallpaper") { const modal = PopoutService.settingsModal;
root.settingsModal.wallpaperBrowser.allowStacking = false; if (modal) {
root.settingsModal.wallpaperBrowser.open(); if (type === "wallpaper") {
} else if (type === "profile") { modal.wallpaperBrowser.allowStacking = false;
root.settingsModal.profileBrowser.allowStacking = false; modal.wallpaperBrowser.open();
root.settingsModal.profileBrowser.open(); } else if (type === "profile") {
modal.profileBrowser.allowStacking = false;
modal.profileBrowser.open();
}
} else {
PopoutService.openSettings();
} }
} }

View File

@@ -160,6 +160,7 @@ Variants {
asynchronous: true asynchronous: true
smooth: true smooth: true
cache: true cache: true
sourceSize: Qt.size(modelData.width, modelData.height)
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SettingsData.wallpaperFillMode) fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SettingsData.wallpaperFillMode)
} }
@@ -171,6 +172,7 @@ Variants {
asynchronous: true asynchronous: true
smooth: true smooth: true
cache: true cache: true
sourceSize: Qt.size(modelData.width, modelData.height)
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SettingsData.wallpaperFillMode) fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SettingsData.wallpaperFillMode)
onStatusChanged: { onStatusChanged: {
@@ -218,14 +220,16 @@ Variants {
duration: 1000 duration: 1000
easing.type: Easing.InOutCubic easing.type: Easing.InOutCubic
onFinished: { onFinished: {
const tempSource = nextWallpaper.source;
if (tempSource && nextWallpaper.status === Image.Ready && !tempSource.toString().startsWith("#")) {
currentWallpaper.source = tempSource;
}
root.transitionProgress = 0.0;
currentWallpaper.opacity = 1;
nextWallpaper.opacity = 0;
Qt.callLater(() => { Qt.callLater(() => {
if (nextWallpaper.source && nextWallpaper.status === Image.Ready && !nextWallpaper.source.toString().startsWith("#")) {
currentWallpaper.source = nextWallpaper.source;
}
nextWallpaper.source = ""; nextWallpaper.source = "";
currentWallpaper.opacity = 1;
nextWallpaper.opacity = 0;
root.transitionProgress = 0.0;
}); });
} }
} }

View File

@@ -99,7 +99,7 @@ Rectangle {
backgroundColor: "transparent" backgroundColor: "transparent"
onClicked: { onClicked: {
root.settingsButtonClicked(); root.settingsButtonClicked();
settingsModal.show(); PopoutService.openSettings();
} }
} }

View File

@@ -28,32 +28,9 @@ Item {
readonly property real wing: gothEnabled ? barWindow._wingR : 0 readonly property real wing: gothEnabled ? barWindow._wingR : 0
readonly property real rt: (barConfig?.squareCorners ?? false) ? 0 : Theme.cornerRadius readonly property real rt: (barConfig?.squareCorners ?? false) ? 0 : Theme.cornerRadius
property string _cachedMainPath: "" readonly property string mainPath: generatePathForPosition(width, height)
property string _cachedBorderFullPath: "" readonly property string borderFullPath: generateBorderFullPath(width, height)
property string _cachedBorderEdgePath: "" readonly property string borderEdgePath: generateBorderEdgePath(width, height)
property string _pathKey: ""
readonly property string currentPathKey: `${width}|${height}|${barPos}|${wing}|${rt}|${barBorder.inset}`
onCurrentPathKeyChanged: {
if (_pathKey !== currentPathKey) {
_pathKey = currentPathKey;
_cachedMainPath = generatePathForPosition();
_cachedBorderFullPath = generateBorderFullPath();
_cachedBorderEdgePath = generateBorderEdgePath();
}
}
Component.onCompleted: {
_pathKey = currentPathKey;
_cachedMainPath = generatePathForPosition();
_cachedBorderFullPath = generateBorderFullPath();
_cachedBorderEdgePath = generateBorderEdgePath();
}
readonly property string mainPath: _cachedMainPath
readonly property string borderFullPath: _cachedBorderFullPath
readonly property string borderEdgePath: _cachedBorderEdgePath
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@@ -90,24 +67,6 @@ Item {
} }
} }
Shape {
id: barTint
anchors.fill: parent
preferredRendererType: Shape.CurveRenderer
readonly property real alphaTint: (barWindow._bgColor?.a ?? 1) < 0.99 ? (Theme.stateLayerOpacity ?? 0) : 0
ShapePath {
fillColor: Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, barTint.alphaTint)
strokeColor: "transparent"
strokeWidth: 0
PathSvg {
path: root.mainPath
}
}
}
Shape { Shape {
id: barBorder id: barBorder
anchors.fill: parent anchors.fill: parent
@@ -134,16 +93,16 @@ Item {
} }
} }
function generatePathForPosition() { function generatePathForPosition(w, h) {
if (isTop) if (isTop)
return generateTopPath(); return generateTopPath(w, h);
if (isBottom) if (isBottom)
return generateBottomPath(); return generateBottomPath(w, h);
if (isLeft) if (isLeft)
return generateLeftPath(); return generateLeftPath(w, h);
if (isRight) if (isRight)
return generateRightPath(); return generateRightPath(w, h);
return generateTopPath(); return generateTopPath(w, h);
} }
function generateBorderPathForPosition() { function generateBorderPathForPosition() {
@@ -158,15 +117,11 @@ Item {
return generateTopBorderPath(); return generateTopBorderPath();
} }
function generateTopPath() { function generateTopPath(w, h) {
const w = width; h = h - wing;
const h = height - wing;
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (w <= 0 || h <= 0)
return "";
let d = `M ${cr} 0`; let d = `M ${cr} 0`;
d += ` L ${w - cr} 0`; d += ` L ${w - cr} 0`;
if (cr > 0) if (cr > 0)
@@ -191,19 +146,16 @@ Item {
return d; return d;
} }
function generateBottomPath() { function generateBottomPath(w, h) {
const w = width; const fullH = h;
const h = height - wing; h = h - wing;
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (w <= 0 || h <= 0) let d = `M ${cr} ${fullH}`;
return ""; d += ` L ${w - cr} ${fullH}`;
let d = `M ${cr} ${height}`;
d += ` L ${w - cr} ${height}`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${w} ${height - cr}`; d += ` A ${cr} ${cr} 0 0 0 ${w} ${fullH - cr}`;
if (r > 0) { if (r > 0) {
d += ` L ${w} 0`; d += ` L ${w} 0`;
d += ` A ${r} ${r} 0 0 1 ${w - r} ${r}`; d += ` A ${r} ${r} 0 0 1 ${w - r} ${r}`;
@@ -217,22 +169,18 @@ Item {
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`; d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`;
} }
d += ` L 0 ${height - cr}`; d += ` L 0 ${fullH - cr}`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${cr} ${height}`; d += ` A ${cr} ${cr} 0 0 0 ${cr} ${fullH}`;
d += " Z"; d += " Z";
return d; return d;
} }
function generateLeftPath() { function generateLeftPath(w, h) {
const w = width - wing; w = w - wing;
const h = height;
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (w <= 0 || h <= 0)
return "";
let d = `M 0 ${cr}`; let d = `M 0 ${cr}`;
d += ` L 0 ${h - cr}`; d += ` L 0 ${h - cr}`;
if (cr > 0) if (cr > 0)
@@ -257,19 +205,16 @@ Item {
return d; return d;
} }
function generateRightPath() { function generateRightPath(w, h) {
const w = width - wing; const fullW = w;
const h = height; w = w - wing;
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (w <= 0 || h <= 0) let d = `M ${fullW} ${cr}`;
return ""; d += ` L ${fullW} ${h - cr}`;
let d = `M ${width} ${cr}`;
d += ` L ${width} ${h - cr}`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${width - cr} ${h}`; d += ` A ${cr} ${cr} 0 0 1 ${fullW - cr} ${h}`;
if (r > 0) { if (r > 0) {
d += ` L 0 ${h}`; d += ` L 0 ${h}`;
d += ` A ${r} ${r} 0 0 0 ${r} ${h - r}`; d += ` A ${r} ${r} 0 0 0 ${r} ${h - r}`;
@@ -283,9 +228,9 @@ Item {
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`; d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`;
} }
d += ` L ${width - cr} 0`; d += ` L ${fullW - cr} 0`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${width} ${cr}`; d += ` A ${cr} ${cr} 0 0 1 ${fullW} ${cr}`;
d += " Z"; d += " Z";
return d; return d;
} }
@@ -296,9 +241,6 @@ Item {
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (w <= 0 || h <= 0)
return "";
let d = ""; let d = "";
if (r > 0) { if (r > 0) {
d = `M ${w} ${h + r}`; d = `M ${w} ${h + r}`;
@@ -321,9 +263,6 @@ Item {
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (w <= 0)
return "";
let d = ""; let d = "";
if (r > 0) { if (r > 0) {
d = `M ${w} 0`; d = `M ${w} 0`;
@@ -347,9 +286,6 @@ Item {
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (h <= 0)
return "";
let d = ""; let d = "";
if (r > 0) { if (r > 0) {
d = `M ${w + r} ${h}`; d = `M ${w + r} ${h}`;
@@ -372,9 +308,6 @@ Item {
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (h <= 0)
return "";
let d = ""; let d = "";
if (r > 0) { if (r > 0) {
d = `M 0 ${h}`; d = `M 0 ${h}`;
@@ -392,26 +325,24 @@ Item {
return d; return d;
} }
function generateBorderFullPath() { function generateBorderFullPath(fullW, fullH) {
const i = barBorder.inset; const i = barBorder.inset;
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (isTop) { if (isTop) {
const w = width - i * 2; const w = fullW - i * 2;
const h = height - wing - i * 2; const h = fullH - wing - i * 2;
if (w <= 0 || h <= 0)
return "";
let d = `M ${i + cr} ${i}`; let d = `M ${i + cr} ${i}`;
d += ` L ${i + w - cr} ${i}`; d += ` L ${i + w - cr} ${i}`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${i + w} ${i + cr}`; d += ` A ${cr} ${cr} 0 0 1 ${i + w} ${i + cr}`;
if (r > 0) { if (r > 0) {
d += ` L ${i + w} ${height - i}`; d += ` L ${i + w} ${fullH - i}`;
d += ` A ${r} ${r} 0 0 0 ${i + w - r} ${i + h}`; d += ` A ${r} ${r} 0 0 0 ${i + w - r} ${i + h}`;
d += ` L ${i + r} ${i + h}`; d += ` L ${i + r} ${i + h}`;
d += ` A ${r} ${r} 0 0 0 ${i} ${height - i}`; d += ` A ${r} ${r} 0 0 0 ${i} ${fullH - i}`;
} else { } else {
d += ` L ${i + w} ${i + h - cr}`; d += ` L ${i + w} ${i + h - cr}`;
if (cr > 0) if (cr > 0)
@@ -428,15 +359,13 @@ Item {
} }
if (isBottom) { if (isBottom) {
const w = width - i * 2; const w = fullW - i * 2;
const h = height - wing - i * 2; const h = fullH - wing - i * 2;
if (w <= 0 || h <= 0)
return "";
let d = `M ${i + cr} ${height - i}`; let d = `M ${i + cr} ${fullH - i}`;
d += ` L ${i + w - cr} ${height - i}`; d += ` L ${i + w - cr} ${fullH - i}`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i + w} ${height - i - cr}`; d += ` A ${cr} ${cr} 0 0 0 ${i + w} ${fullH - i - cr}`;
if (r > 0) { if (r > 0) {
d += ` L ${i + w} ${i}`; d += ` L ${i + w} ${i}`;
d += ` A ${r} ${r} 0 0 1 ${i + w - r} ${i + r}`; d += ` A ${r} ${r} 0 0 1 ${i + w - r} ${i + r}`;
@@ -450,28 +379,26 @@ Item {
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i} ${i + cr}`; d += ` A ${cr} ${cr} 0 0 0 ${i} ${i + cr}`;
} }
d += ` L ${i} ${height - i - cr}`; d += ` L ${i} ${fullH - i - cr}`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i + cr} ${height - i}`; d += ` A ${cr} ${cr} 0 0 0 ${i + cr} ${fullH - i}`;
d += " Z"; d += " Z";
return d; return d;
} }
if (isLeft) { if (isLeft) {
const w = width - wing - i * 2; const w = fullW - wing - i * 2;
const h = height - i * 2; const h = fullH - i * 2;
if (w <= 0 || h <= 0)
return "";
let d = `M ${i} ${i + cr}`; let d = `M ${i} ${i + cr}`;
d += ` L ${i} ${i + h - cr}`; d += ` L ${i} ${i + h - cr}`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i + cr} ${i + h}`; d += ` A ${cr} ${cr} 0 0 0 ${i + cr} ${i + h}`;
if (r > 0) { if (r > 0) {
d += ` L ${width - i} ${i + h}`; d += ` L ${fullW - i} ${i + h}`;
d += ` A ${r} ${r} 0 0 1 ${i + w} ${i + h - r}`; d += ` A ${r} ${r} 0 0 1 ${i + w} ${i + h - r}`;
d += ` L ${i + w} ${i + r}`; d += ` L ${i + w} ${i + r}`;
d += ` A ${r} ${r} 0 0 1 ${width - i} ${i}`; d += ` A ${r} ${r} 0 0 1 ${fullW - i} ${i}`;
} else { } else {
d += ` L ${i + w - cr} ${i + h}`; d += ` L ${i + w - cr} ${i + h}`;
if (cr > 0) if (cr > 0)
@@ -488,15 +415,13 @@ Item {
} }
if (isRight) { if (isRight) {
const w = width - wing - i * 2; const w = fullW - wing - i * 2;
const h = height - i * 2; const h = fullH - i * 2;
if (w <= 0 || h <= 0)
return "";
let d = `M ${width - i} ${i + cr}`; let d = `M ${fullW - i} ${i + cr}`;
d += ` L ${width - i} ${i + h - cr}`; d += ` L ${fullW - i} ${i + h - cr}`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${width - i - cr} ${i + h}`; d += ` A ${cr} ${cr} 0 0 1 ${fullW - i - cr} ${i + h}`;
if (r > 0) { if (r > 0) {
d += ` L ${i} ${i + h}`; d += ` L ${i} ${i + h}`;
d += ` A ${r} ${r} 0 0 0 ${i + r} ${i + h - r}`; d += ` A ${r} ${r} 0 0 0 ${i + r} ${i + h - r}`;
@@ -510,9 +435,9 @@ Item {
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${wing + i + cr} ${i}`; d += ` A ${cr} ${cr} 0 0 1 ${wing + i + cr} ${i}`;
} }
d += ` L ${width - i - cr} ${i}`; d += ` L ${fullW - i - cr} ${i}`;
if (cr > 0) if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${width - i} ${i + cr}`; d += ` A ${cr} ${cr} 0 0 1 ${fullW - i} ${i + cr}`;
d += " Z"; d += " Z";
return d; return d;
} }
@@ -520,16 +445,14 @@ Item {
return ""; return "";
} }
function generateBorderEdgePath() { function generateBorderEdgePath(fullW, fullH) {
const i = barBorder.inset; const i = barBorder.inset;
const r = wing; const r = wing;
const cr = rt; const cr = rt;
if (isTop) { if (isTop) {
const w = width - i * 2; const w = fullW - i * 2;
const h = height - wing - i * 2; const h = fullH - wing - i * 2;
if (w <= 0 || h <= 0)
return "";
let d = ""; let d = "";
if (r > 0) { if (r > 0) {
@@ -549,9 +472,7 @@ Item {
} }
if (isBottom) { if (isBottom) {
const w = width - i * 2; const w = fullW - i * 2;
if (w <= 0)
return "";
let d = ""; let d = "";
if (r > 0) { if (r > 0) {
@@ -571,10 +492,8 @@ Item {
} }
if (isLeft) { if (isLeft) {
const w = width - wing - i * 2; const w = fullW - wing - i * 2;
const h = height - i * 2; const h = fullH - i * 2;
if (h <= 0)
return "";
let d = ""; let d = "";
if (r > 0) { if (r > 0) {
@@ -594,9 +513,7 @@ Item {
} }
if (isRight) { if (isRight) {
const h = height - i * 2; const h = fullH - i * 2;
if (h <= 0)
return "";
let d = ""; let d = "";
if (r > 0) { if (r > 0) {

View File

@@ -140,7 +140,9 @@ PanelWindow {
property real wingtipsRadius: barConfig?.gothCornerRadiusOverride ? (barConfig?.gothCornerRadiusValue ?? 12) : Theme.cornerRadius property real wingtipsRadius: barConfig?.gothCornerRadiusOverride ? (barConfig?.gothCornerRadiusValue ?? 12) : Theme.cornerRadius
readonly property real _wingR: Math.max(0, wingtipsRadius) readonly property real _wingR: Math.max(0, wingtipsRadius)
readonly property color _surfaceContainer: Theme.surfaceContainer readonly property color _surfaceContainer: Theme.surfaceContainer
readonly property real _backgroundAlpha: topBarCore?.backgroundTransparency ?? (barConfig?.transparency ?? 1.0) readonly property string _barId: barConfig?.id ?? "default"
readonly property var _liveBarConfig: SettingsData.barConfigs.find(c => c.id === _barId) || barConfig
readonly property real _backgroundAlpha: _liveBarConfig?.transparency ?? 1.0
readonly property color _bgColor: Theme.withAlpha(_surfaceContainer, _backgroundAlpha) readonly property color _bgColor: Theme.withAlpha(_surfaceContainer, _backgroundAlpha)
readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen) readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen)
@@ -419,7 +421,6 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
layer.enabled: true layer.enabled: true
property real backgroundTransparency: barConfig?.transparency ?? 1.0
property bool autoHide: barConfig?.autoHide ?? false property bool autoHide: barConfig?.autoHide ?? false
property bool revealSticky: false property bool revealSticky: false
@@ -471,7 +472,6 @@ PanelWindow {
Connections { Connections {
function onBarConfigChanged() { function onBarConfigChanged() {
topBarCore.backgroundTransparency = barConfig?.transparency ?? 1.0;
topBarCore.autoHide = barConfig?.autoHide ?? false; topBarCore.autoHide = barConfig?.autoHide ?? false;
revealHold.interval = barConfig?.autoHideDelay ?? 250; revealHold.interval = barConfig?.autoHideDelay ?? 250;
} }

View File

@@ -10,6 +10,13 @@ BasePill {
readonly property MprisPlayer activePlayer: MprisController.activePlayer readonly property MprisPlayer activePlayer: MprisController.activePlayer
readonly property bool playerAvailable: activePlayer !== null readonly property bool playerAvailable: activePlayer !== null
readonly property bool __isChromeBrowser: {
if (!activePlayer?.identity)
return false;
const id = activePlayer.identity.toLowerCase();
return id.includes("chrome") || id.includes("chromium");
}
readonly property bool usePlayerVolume: activePlayer && activePlayer.volumeSupported && !__isChromeBrowser
property bool compactMode: false property bool compactMode: false
property var widgetData: null property var widgetData: null
readonly property int textWidth: { readonly property int textWidth: {
@@ -49,14 +56,8 @@ BasePill {
wheelEvent.accepted = true; wheelEvent.accepted = true;
// If volume is not supported, return early to avoid error logs but accepting the scroll,
// to keep the consistency of not scrolling workspaces when scrolling in the media widget.
if (!activePlayer.volumeSupported) {
return;
}
const delta = wheelEvent.angleDelta.y; const delta = wheelEvent.angleDelta.y;
const currentVolume = (activePlayer.volume * 100) || 0; const currentVolume = usePlayerVolume ? (activePlayer.volume * 100) : ((AudioService.sink?.audio?.volume ?? 0) * 100);
let newVolume; let newVolume;
if (delta > 0) { if (delta > 0) {
@@ -65,7 +66,11 @@ BasePill {
newVolume = Math.max(0, currentVolume - 5); newVolume = Math.max(0, currentVolume - 5);
} }
activePlayer.volume = newVolume / 100; if (usePlayerVolume) {
activePlayer.volume = newVolume / 100;
} else if (AudioService.sink?.audio) {
AudioService.sink.audio.volume = newVolume / 100;
}
} }
content: Component { content: Component {

View File

@@ -301,7 +301,7 @@ DankPopout {
let settingsIndex = SettingsData.weatherEnabled ? 4 : 3; let settingsIndex = SettingsData.weatherEnabled ? 4 : 3;
if (index === settingsIndex) { if (index === settingsIndex) {
dashVisible = false; dashVisible = false;
settingsModal.show(); PopoutService.openSettings();
} }
} }
} }

View File

@@ -10,6 +10,18 @@ DankOSD {
readonly property var player: MprisController.activePlayer readonly property var player: MprisController.activePlayer
readonly property int currentVolume: player ? Math.min(100, Math.round(player.volume * 100)) : 0 readonly property int currentVolume: player ? Math.min(100, Math.round(player.volume * 100)) : 0
readonly property bool volumeSupported: player?.volumeSupported ?? false readonly property bool volumeSupported: player?.volumeSupported ?? false
property bool _suppressNewPlayer: false
onPlayerChanged: {
_suppressNewPlayer = true;
_suppressTimer.restart();
}
Timer {
id: _suppressTimer
interval: 2000
onTriggered: _suppressNewPlayer = false
}
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2) 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) osdHeight: useVertical ? Math.min(260, Screen.height - Theme.spacingM * 2) : (40 + Theme.spacingS * 2)
@@ -41,7 +53,7 @@ DankOSD {
target: player target: player
function onVolumeChanged() { function onVolumeChanged() {
if (SettingsData.osdMediaVolumeEnabled && volumeSupported) { if (SettingsData.osdMediaVolumeEnabled && volumeSupported && !_suppressNewPlayer) {
root.show(); root.show();
} }
} }

View File

@@ -75,7 +75,7 @@ DankPopout {
} }
} }
if (root.contentHeight === 0 && item) { if (root.contentHeight === 0 && item) {
root.contentHeight = item.implicitHeight + Theme.spacingS * 2 root.contentHeight = Qt.binding(() => item.implicitHeight + Theme.spacingS * 2)
} }
} }
} }

View File

@@ -2,19 +2,16 @@ import QtQuick
import QtQuick.Effects import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Widgets
import Quickshell.Io
import qs.Common import qs.Common
import qs.Widgets import qs.Widgets
import qs.Modules
import qs.Services import qs.Services
Variants { Variants {
model: { model: {
if (SessionData.isGreeterMode) { if (SessionData.isGreeterMode) {
return Quickshell.screens return Quickshell.screens;
} }
return SettingsData.getFilteredScreens("wallpaper") return SettingsData.getFilteredScreens("wallpaper");
} }
PanelWindow { PanelWindow {
@@ -52,9 +49,9 @@ Variants {
target: SessionData target: SessionData
function onIsLightModeChanged() { function onIsLightModeChanged() {
if (SessionData.perModeWallpaper) { if (SessionData.perModeWallpaper) {
var newSource = SessionData.getMonitorWallpaper(modelData.name) || "" var newSource = SessionData.getMonitorWallpaper(modelData.name) || "";
if (newSource !== root.source) { if (newSource !== root.source) {
root.source = newSource root.source = newSource;
} }
} }
} }
@@ -62,19 +59,19 @@ Variants {
onTransitionTypeChanged: { onTransitionTypeChanged: {
if (transitionType === "random") { if (transitionType === "random") {
if (SessionData.includedTransitions.length === 0) { if (SessionData.includedTransitions.length === 0) {
actualTransitionType = "none" actualTransitionType = "none";
} else { } else {
actualTransitionType = SessionData.includedTransitions[Math.floor(Math.random() * SessionData.includedTransitions.length)] actualTransitionType = SessionData.includedTransitions[Math.floor(Math.random() * SessionData.includedTransitions.length)];
} }
} else { } else {
actualTransitionType = transitionType actualTransitionType = transitionType;
} }
} }
onActualTransitionTypeChanged: { onActualTransitionTypeChanged: {
if (actualTransitionType === "none") { if (actualTransitionType === "none") {
currentWallpaper.visible = true currentWallpaper.visible = true;
nextWallpaper.visible = false nextWallpaper.visible = false;
} }
} }
property real transitionProgress: 0 property real transitionProgress: 0
@@ -94,103 +91,110 @@ Variants {
property bool booting: !hasCurrent && nextWallpaper.status === Image.Ready property bool booting: !hasCurrent && nextWallpaper.status === Image.Ready
function getFillMode(modeName) { function getFillMode(modeName) {
switch(modeName) { switch (modeName) {
case "Stretch": return Image.Stretch case "Stretch":
case "Fit": return Image.Stretch;
case "PreserveAspectFit": return Image.PreserveAspectFit case "Fit":
case "Fill": case "PreserveAspectFit":
case "PreserveAspectCrop": return Image.PreserveAspectCrop return Image.PreserveAspectFit;
case "Tile": return Image.Tile case "Fill":
case "TileVertically": return Image.TileVertically case "PreserveAspectCrop":
case "TileHorizontally": return Image.TileHorizontally return Image.PreserveAspectCrop;
case "Pad": return Image.Pad case "Tile":
default: return Image.PreserveAspectCrop return Image.Tile;
case "TileVertically":
return Image.TileVertically;
case "TileHorizontally":
return Image.TileHorizontally;
case "Pad":
return Image.Pad;
default:
return Image.PreserveAspectCrop;
} }
} }
Component.onCompleted: { Component.onCompleted: {
if (source) { if (source) {
const formattedSource = source.startsWith("file://") ? source : "file://" + source const formattedSource = source.startsWith("file://") ? source : "file://" + source;
setWallpaperImmediate(formattedSource) setWallpaperImmediate(formattedSource);
} }
isInitialized = true isInitialized = true;
} }
onSourceChanged: { onSourceChanged: {
const isColor = source.startsWith("#") const isColor = source.startsWith("#");
if (!source) { if (!source) {
setWallpaperImmediate("") setWallpaperImmediate("");
} else if (isColor) { } else if (isColor) {
setWallpaperImmediate("") setWallpaperImmediate("");
} else { } else {
if (!isInitialized || !currentWallpaper.source) { if (!isInitialized || !currentWallpaper.source) {
setWallpaperImmediate(source.startsWith("file://") ? source : "file://" + source) setWallpaperImmediate(source.startsWith("file://") ? source : "file://" + source);
isInitialized = true isInitialized = true;
} else if (CompositorService.isNiri && SessionData.isSwitchingMode) { } else if (CompositorService.isNiri && SessionData.isSwitchingMode) {
setWallpaperImmediate(source.startsWith("file://") ? source : "file://" + source) setWallpaperImmediate(source.startsWith("file://") ? source : "file://" + source);
} else { } else {
changeWallpaper(source.startsWith("file://") ? source : "file://" + source) changeWallpaper(source.startsWith("file://") ? source : "file://" + source);
} }
} }
} }
function setWallpaperImmediate(newSource) { function setWallpaperImmediate(newSource) {
transitionAnimation.stop() transitionAnimation.stop();
root.transitionProgress = 0.0 root.transitionProgress = 0.0;
currentWallpaper.source = newSource currentWallpaper.source = newSource;
nextWallpaper.source = "" nextWallpaper.source = "";
currentWallpaper.visible = true currentWallpaper.visible = true;
nextWallpaper.visible = false nextWallpaper.visible = false;
} }
function changeWallpaper(newPath, force) { function changeWallpaper(newPath, force) {
if (!force && newPath === currentWallpaper.source) if (!force && newPath === currentWallpaper.source)
return return;
if (!newPath || newPath.startsWith("#")) if (!newPath || newPath.startsWith("#"))
return return;
if (root.transitioning) { if (root.transitioning) {
transitionAnimation.stop() transitionAnimation.stop();
root.transitionProgress = 0 root.transitionProgress = 0;
currentWallpaper.source = nextWallpaper.source currentWallpaper.source = nextWallpaper.source;
nextWallpaper.source = "" nextWallpaper.source = "";
} }
// If no current wallpaper, set immediately to avoid scaling issues // If no current wallpaper, set immediately to avoid scaling issues
if (!currentWallpaper.source) { if (!currentWallpaper.source) {
setWallpaperImmediate(newPath) setWallpaperImmediate(newPath);
return return;
} }
// If transition is "none", set immediately // If transition is "none", set immediately
if (root.transitionType === "random") { if (root.transitionType === "random") {
if (SessionData.includedTransitions.length === 0) { if (SessionData.includedTransitions.length === 0) {
root.actualTransitionType = "none" root.actualTransitionType = "none";
} else { } else {
root.actualTransitionType = SessionData.includedTransitions[Math.floor(Math.random() * SessionData.includedTransitions.length)] root.actualTransitionType = SessionData.includedTransitions[Math.floor(Math.random() * SessionData.includedTransitions.length)];
} }
} }
if (root.actualTransitionType === "none") { if (root.actualTransitionType === "none") {
setWallpaperImmediate(newPath) setWallpaperImmediate(newPath);
return return;
} }
if (root.actualTransitionType === "wipe") { if (root.actualTransitionType === "wipe") {
root.wipeDirection = Math.random() * 4 root.wipeDirection = Math.random() * 4;
} else if (root.actualTransitionType === "disc") { } else if (root.actualTransitionType === "disc") {
root.discCenterX = Math.random() root.discCenterX = Math.random();
root.discCenterY = Math.random() root.discCenterY = Math.random();
} else if (root.actualTransitionType === "stripes") { } else if (root.actualTransitionType === "stripes") {
root.stripesCount = Math.round(Math.random() * 20 + 4) root.stripesCount = Math.round(Math.random() * 20 + 4);
root.stripesAngle = Math.random() * 360 root.stripesAngle = Math.random() * 360;
} }
nextWallpaper.source = newPath nextWallpaper.source = newPath;
if (nextWallpaper.status === Image.Ready) { if (nextWallpaper.status === Image.Ready) {
transitionAnimation.start() transitionAnimation.start();
} }
} }
@@ -227,6 +231,7 @@ Variants {
asynchronous: true asynchronous: true
smooth: true smooth: true
cache: true cache: true
sourceSize: Qt.size(modelData.width, modelData.height)
fillMode: root.getFillMode(SettingsData.wallpaperFillMode) fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
} }
@@ -239,20 +244,20 @@ Variants {
asynchronous: true asynchronous: true
smooth: true smooth: true
cache: true cache: true
sourceSize: Qt.size(modelData.width, modelData.height)
fillMode: root.getFillMode(SettingsData.wallpaperFillMode) fillMode: root.getFillMode(SettingsData.wallpaperFillMode)
onStatusChanged: { onStatusChanged: {
if (status !== Image.Ready) if (status !== Image.Ready)
return return;
if (root.actualTransitionType === "none") { if (root.actualTransitionType === "none") {
currentWallpaper.source = source currentWallpaper.source = source;
nextWallpaper.source = "" nextWallpaper.source = "";
root.transitionProgress = 0.0 root.transitionProgress = 0.0;
} else { } else {
visible = true visible = true;
if (!root.transitioning) { if (!root.transitioning) {
transitionAnimation.start() transitionAnimation.start();
} }
} }
} }
@@ -265,21 +270,21 @@ Variants {
sourceComponent: { sourceComponent: {
switch (root.actualTransitionType) { switch (root.actualTransitionType) {
case "fade": case "fade":
return fadeComp return fadeComp;
case "wipe": case "wipe":
return wipeComp return wipeComp;
case "disc": case "disc":
return discComp return discComp;
case "stripes": case "stripes":
return stripesComp return stripesComp;
case "iris bloom": case "iris bloom":
return irisComp return irisComp;
case "pixelate": case "pixelate":
return pixelateComp return pixelateComp;
case "portal": case "portal":
return portalComp return portalComp;
default: default:
return null return null;
} }
} }
} }
@@ -448,15 +453,17 @@ Variants {
duration: root.actualTransitionType === "none" ? 0 : 1000 duration: root.actualTransitionType === "none" ? 0 : 1000
easing.type: Easing.InOutCubic easing.type: Easing.InOutCubic
onFinished: { onFinished: {
const tempSource = nextWallpaper.source;
if (tempSource && nextWallpaper.status === Image.Ready && !tempSource.toString().startsWith("#")) {
currentWallpaper.source = tempSource;
}
root.transitionProgress = 0.0;
currentWallpaper.visible = root.actualTransitionType === "none";
Qt.callLater(() => { Qt.callLater(() => {
if (nextWallpaper.source && nextWallpaper.status === Image.Ready && !nextWallpaper.source.toString().startsWith("#")) { nextWallpaper.source = "";
currentWallpaper.source = nextWallpaper.source nextWallpaper.visible = false;
} });
nextWallpaper.source = ""
nextWallpaper.visible = false
currentWallpaper.visible = root.actualTransitionType === "none"
root.transitionProgress = 0.0
})
} }
} }

View File

@@ -1,11 +1,9 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Services.Notifications import Quickshell.Services.Notifications
import Quickshell.Widgets
import qs.Common import qs.Common
import "../Common/markdown2html.js" as Markdown2Html import "../Common/markdown2html.js" as Markdown2Html
@@ -28,108 +26,106 @@ Singleton {
property int maxIngressPerSecond: 20 property int maxIngressPerSecond: 20
property double _lastIngressSec: 0 property double _lastIngressSec: 0
property int _ingressCountThisSec: 0 property int _ingressCountThisSec: 0
property int maxStoredNotifications: 300 property int maxStoredNotifications: 50
property var _dismissQueue: [] property var _dismissQueue: []
property int _dismissBatchSize: 8 property int _dismissBatchSize: 8
property int _dismissTickMs: 8 property int _dismissTickMs: 8
property bool _suspendGrouping: false property bool _suspendGrouping: false
property var _groupCache: ({ property var _groupCache: ({
"notifications": [], "notifications": [],
"popups": [] "popups": []
}) })
property bool _groupsDirty: false property bool _groupsDirty: false
Component.onCompleted: { Component.onCompleted: {
_recomputeGroups() _recomputeGroups();
} }
function _nowSec() { function _nowSec() {
return Date.now() / 1000.0 return Date.now() / 1000.0;
} }
function _ingressAllowed(notif) { function _ingressAllowed(notif) {
const t = _nowSec() const t = _nowSec();
if (t - _lastIngressSec >= 1.0) { if (t - _lastIngressSec >= 1.0) {
_lastIngressSec = t _lastIngressSec = t;
_ingressCountThisSec = 0 _ingressCountThisSec = 0;
} }
_ingressCountThisSec += 1 _ingressCountThisSec += 1;
if (notif.urgency === NotificationUrgency.Critical) { if (notif.urgency === NotificationUrgency.Critical) {
return true return true;
} }
return _ingressCountThisSec <= maxIngressPerSecond return _ingressCountThisSec <= maxIngressPerSecond;
} }
function _enqueuePopup(wrapper) { function _enqueuePopup(wrapper) {
if (notificationQueue.length >= maxQueueSize) { if (notificationQueue.length >= maxQueueSize) {
const gk = getGroupKey(wrapper) const gk = getGroupKey(wrapper);
let idx = notificationQueue.findIndex(w => w && getGroupKey(w) === gk && w.urgency !== NotificationUrgency.Critical) let idx = notificationQueue.findIndex(w => w && getGroupKey(w) === gk && w.urgency !== NotificationUrgency.Critical);
if (idx === -1) { if (idx === -1) {
idx = notificationQueue.findIndex(w => w && w.urgency !== NotificationUrgency.Critical) idx = notificationQueue.findIndex(w => w && w.urgency !== NotificationUrgency.Critical);
} }
if (idx === -1) { if (idx === -1) {
idx = 0 idx = 0;
} }
const victim = notificationQueue[idx] const victim = notificationQueue[idx];
if (victim) { if (victim) {
victim.popup = false victim.popup = false;
} }
notificationQueue.splice(idx, 1) notificationQueue.splice(idx, 1);
} }
notificationQueue = [...notificationQueue, wrapper] notificationQueue = [...notificationQueue, wrapper];
} }
function _initWrapperPersistence(wrapper) { function _initWrapperPersistence(wrapper) {
const timeoutMs = wrapper.timer ? wrapper.timer.interval : 5000 const timeoutMs = wrapper.timer ? wrapper.timer.interval : 5000;
const isCritical = wrapper.notification && wrapper.notification.urgency === NotificationUrgency.Critical const isCritical = wrapper.notification && wrapper.notification.urgency === NotificationUrgency.Critical;
wrapper.isPersistent = isCritical || (timeoutMs === 0) wrapper.isPersistent = isCritical || (timeoutMs === 0);
} }
function _trimStored() { function _trimStored() {
if (notifications.length > maxStoredNotifications) { if (notifications.length > maxStoredNotifications) {
const overflow = notifications.length - maxStoredNotifications const overflow = notifications.length - maxStoredNotifications;
const toDrop = [] const toDrop = [];
for (var i = notifications.length - 1; i >= 0 && toDrop.length < overflow; --i) { for (var i = notifications.length - 1; i >= 0 && toDrop.length < overflow; --i) {
const w = notifications[i] const w = notifications[i];
if (w && w.notification && w.urgency !== NotificationUrgency.Critical) { if (w && w.notification && w.urgency !== NotificationUrgency.Critical) {
toDrop.push(w) toDrop.push(w);
} }
} }
for (var i = notifications.length - 1; i >= 0 && toDrop.length < overflow; --i) { for (var i = notifications.length - 1; i >= 0 && toDrop.length < overflow; --i) {
const w = notifications[i] const w = notifications[i];
if (w && w.notification && toDrop.indexOf(w) === -1) { if (w && w.notification && toDrop.indexOf(w) === -1) {
toDrop.push(w) toDrop.push(w);
} }
} }
for (const w of toDrop) { for (const w of toDrop) {
try { try {
w.notification.dismiss() w.notification.dismiss();
} catch (e) { } catch (e) {}
}
} }
} }
} }
function onOverlayOpen() { function onOverlayOpen() {
popupsDisabled = true popupsDisabled = true;
addGate.stop() addGate.stop();
addGateBusy = false addGateBusy = false;
notificationQueue = [] notificationQueue = [];
for (const w of visibleNotifications) { for (const w of visibleNotifications) {
if (w) { if (w) {
w.popup = false w.popup = false;
} }
} }
visibleNotifications = [] visibleNotifications = [];
_recomputeGroupsLater() _recomputeGroupsLater();
} }
function onOverlayClose() { function onOverlayClose() {
popupsDisabled = false popupsDisabled = false;
processQueue() processQueue();
} }
Timer { Timer {
@@ -138,8 +134,8 @@ Singleton {
running: false running: false
repeat: false repeat: false
onTriggered: { onTriggered: {
addGateBusy = false addGateBusy = false;
processQueue() processQueue();
} }
} }
@@ -150,7 +146,7 @@ Singleton {
running: root.allWrappers.length > 0 || visibleNotifications.length > 0 running: root.allWrappers.length > 0 || visibleNotifications.length > 0
triggeredOnStart: false triggeredOnStart: false
onTriggered: { onTriggered: {
root.timeUpdateTick = !root.timeUpdateTick root.timeUpdateTick = !root.timeUpdateTick;
} }
} }
@@ -160,23 +156,21 @@ Singleton {
repeat: true repeat: true
running: false running: false
onTriggered: { onTriggered: {
let n = Math.min(_dismissBatchSize, _dismissQueue.length) let n = Math.min(_dismissBatchSize, _dismissQueue.length);
for (var i = 0; i < n; ++i) { for (var i = 0; i < n; ++i) {
const w = _dismissQueue.pop() const w = _dismissQueue.pop();
try { try {
if (w && w.notification) { if (w && w.notification) {
w.notification.dismiss() w.notification.dismiss();
} }
} catch (e) { } catch (e) {}
}
} }
if (_dismissQueue.length === 0) { if (_dismissQueue.length === 0) {
dismissPump.stop() dismissPump.stop();
_suspendGrouping = false _suspendGrouping = false;
bulkDismissing = false bulkDismissing = false;
popupsDisabled = false popupsDisabled = false;
_recomputeGroupsLater() _recomputeGroupsLater();
} }
} }
} }
@@ -212,52 +206,50 @@ Singleton {
persistenceSupported: true persistenceSupported: true
onNotification: notif => { onNotification: notif => {
notif.tracked = true notif.tracked = true;
if (!_ingressAllowed(notif)) { if (!_ingressAllowed(notif)) {
if (notif.urgency !== NotificationUrgency.Critical) { if (notif.urgency !== NotificationUrgency.Critical) {
try { try {
notif.dismiss() notif.dismiss();
} catch (e) { } catch (e) {}
return;
}
return
} }
} }
if (SettingsData.soundsEnabled && SettingsData.soundNewNotification) { if (SettingsData.soundsEnabled && SettingsData.soundNewNotification) {
if (notif.urgency === NotificationUrgency.Critical) { if (notif.urgency === NotificationUrgency.Critical) {
AudioService.playCriticalNotificationSound() AudioService.playCriticalNotificationSound();
} else { } else {
AudioService.playNormalNotificationSound() AudioService.playNormalNotificationSound();
} }
} }
const shouldShowPopup = !root.popupsDisabled && !SessionData.doNotDisturb const shouldShowPopup = !root.popupsDisabled && !SessionData.doNotDisturb;
const isTransient = notif.transient const isTransient = notif.transient;
const wrapper = notifComponent.createObject(root, { const wrapper = notifComponent.createObject(root, {
"popup": shouldShowPopup, "popup": shouldShowPopup,
"notification": notif "notification": notif
}) });
if (wrapper) { if (wrapper) {
root.allWrappers.push(wrapper) root.allWrappers.push(wrapper);
if (!isTransient) { if (!isTransient) {
root.notifications.push(wrapper) root.notifications.push(wrapper);
_trimStored() _trimStored();
} }
Qt.callLater(() => { Qt.callLater(() => {
_initWrapperPersistence(wrapper) _initWrapperPersistence(wrapper);
}) });
if (shouldShowPopup) { if (shouldShowPopup) {
_enqueuePopup(wrapper) _enqueuePopup(wrapper);
processQueue() processQueue();
} }
} }
_recomputeGroupsLater() _recomputeGroupsLater();
} }
} }
@@ -271,80 +263,80 @@ Singleton {
onPopupChanged: { onPopupChanged: {
if (!popup) { if (!popup) {
removeFromVisibleNotifications(wrapper) removeFromVisibleNotifications(wrapper);
} }
} }
readonly property Timer timer: Timer { readonly property Timer timer: Timer {
interval: { interval: {
if (!wrapper.notification) { if (!wrapper.notification) {
return 5000 return 5000;
} }
switch (wrapper.notification.urgency) { switch (wrapper.notification.urgency) {
case NotificationUrgency.Low: case NotificationUrgency.Low:
return SettingsData.notificationTimeoutLow return SettingsData.notificationTimeoutLow;
case NotificationUrgency.Critical: case NotificationUrgency.Critical:
return SettingsData.notificationTimeoutCritical return SettingsData.notificationTimeoutCritical;
default: default:
return SettingsData.notificationTimeoutNormal return SettingsData.notificationTimeoutNormal;
} }
} }
repeat: false repeat: false
running: false running: false
onTriggered: { onTriggered: {
if (interval > 0) { if (interval > 0) {
wrapper.popup = false wrapper.popup = false;
} }
} }
} }
readonly property date time: new Date() readonly property date time: new Date()
readonly property string timeStr: { readonly property string timeStr: {
root.timeUpdateTick root.timeUpdateTick;
root.clockFormatChanged root.clockFormatChanged;
const now = new Date() const now = new Date();
const diff = now.getTime() - time.getTime() const diff = now.getTime() - time.getTime();
const minutes = Math.floor(diff / 60000) const minutes = Math.floor(diff / 60000);
const hours = Math.floor(minutes / 60) const hours = Math.floor(minutes / 60);
if (hours < 1) { if (hours < 1) {
if (minutes < 1) { if (minutes < 1) {
return "now" return "now";
} }
return `${minutes}m ago` return `${minutes}m ago`;
} }
const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()) const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate()) const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate());
const daysDiff = Math.floor((nowDate - timeDate) / (1000 * 60 * 60 * 24)) const daysDiff = Math.floor((nowDate - timeDate) / (1000 * 60 * 60 * 24));
if (daysDiff === 0) { if (daysDiff === 0) {
return formatTime(time) return formatTime(time);
} }
if (daysDiff === 1) { if (daysDiff === 1) {
return `yesterday, ${formatTime(time)}` return `yesterday, ${formatTime(time)}`;
} }
return `${daysDiff} days ago` return `${daysDiff} days ago`;
} }
function formatTime(date) { function formatTime(date) {
let use24Hour = true let use24Hour = true;
try { try {
if (typeof SettingsData !== "undefined" && SettingsData.use24HourClock !== undefined) { if (typeof SettingsData !== "undefined" && SettingsData.use24HourClock !== undefined) {
use24Hour = SettingsData.use24HourClock use24Hour = SettingsData.use24HourClock;
} }
} catch (e) { } catch (e) {
use24Hour = true use24Hour = true;
} }
if (use24Hour) { if (use24Hour) {
return date.toLocaleTimeString(Qt.locale(), "HH:mm") return date.toLocaleTimeString(Qt.locale(), "HH:mm");
} else { } else {
return date.toLocaleTimeString(Qt.locale(), "h:mm AP") return date.toLocaleTimeString(Qt.locale(), "h:mm AP");
} }
} }
@@ -353,27 +345,27 @@ Singleton {
readonly property string body: notification.body readonly property string body: notification.body
readonly property string htmlBody: { readonly property string htmlBody: {
if (body && (body.includes('<') && body.includes('>'))) { if (body && (body.includes('<') && body.includes('>'))) {
return body return body;
} }
return Markdown2Html.markdownToHtml(body) return Markdown2Html.markdownToHtml(body);
} }
readonly property string appIcon: notification.appIcon readonly property string appIcon: notification.appIcon
readonly property string appName: { readonly property string appName: {
if (notification.appName == "") { if (notification.appName == "") {
const entry = DesktopEntries.heuristicLookup(notification.desktopEntry) const entry = DesktopEntries.heuristicLookup(notification.desktopEntry);
if (entry && entry.name) { if (entry && entry.name) {
return entry.name.toLowerCase() return entry.name.toLowerCase();
} }
} }
return notification.appName || "app" return notification.appName || "app";
} }
readonly property string desktopEntry: notification.desktopEntry readonly property string desktopEntry: notification.desktopEntry
readonly property string image: notification.image readonly property string image: notification.image
readonly property string cleanImage: { readonly property string cleanImage: {
if (!image) { if (!image) {
return "" return "";
} }
return Paths.strip(image) return Paths.strip(image);
} }
readonly property int urgency: notification.urgency readonly property int urgency: notification.urgency
readonly property list<NotificationAction> actions: notification.actions readonly property list<NotificationAction> actions: notification.actions
@@ -382,26 +374,26 @@ Singleton {
target: wrapper.notification.Retainable target: wrapper.notification.Retainable
function onDropped(): void { function onDropped(): void {
root.allWrappers = root.allWrappers.filter(w => w !== wrapper) root.allWrappers = root.allWrappers.filter(w => w !== wrapper);
root.notifications = root.notifications.filter(w => w !== wrapper) root.notifications = root.notifications.filter(w => w !== wrapper);
if (root.bulkDismissing) { if (root.bulkDismissing) {
return return;
} }
const groupKey = getGroupKey(wrapper) const groupKey = getGroupKey(wrapper);
const remainingInGroup = root.notifications.filter(n => getGroupKey(n) === groupKey) const remainingInGroup = root.notifications.filter(n => getGroupKey(n) === groupKey);
if (remainingInGroup.length <= 1) { if (remainingInGroup.length <= 1) {
clearGroupExpansionState(groupKey) clearGroupExpansionState(groupKey);
} }
cleanupExpansionStates() cleanupExpansionStates();
root._recomputeGroupsLater() root._recomputeGroupsLater();
} }
function onAboutToDestroy(): void { function onAboutToDestroy(): void {
wrapper.destroy() wrapper.destroy();
} }
} }
} }
@@ -414,146 +406,145 @@ Singleton {
function clearAllPopups() { function clearAllPopups() {
for (const w of visibleNotifications) { for (const w of visibleNotifications) {
if (w) { if (w) {
w.popup = false w.popup = false;
} }
} }
visibleNotifications = [] visibleNotifications = [];
notificationQueue = [] notificationQueue = [];
} }
function clearAllNotifications() { function clearAllNotifications() {
bulkDismissing = true bulkDismissing = true;
popupsDisabled = true popupsDisabled = true;
addGate.stop() addGate.stop();
addGateBusy = false addGateBusy = false;
notificationQueue = [] notificationQueue = [];
for (const w of allWrappers) { for (const w of allWrappers) {
if (w) { if (w) {
w.popup = false w.popup = false;
} }
} }
visibleNotifications = [] visibleNotifications = [];
_dismissQueue = notifications.slice() _dismissQueue = notifications.slice();
if (notifications.length) { if (notifications.length) {
notifications = [] notifications = [];
} }
expandedGroups = {} expandedGroups = {};
expandedMessages = {} expandedMessages = {};
_suspendGrouping = true _suspendGrouping = true;
if (!dismissPump.running && _dismissQueue.length) { if (!dismissPump.running && _dismissQueue.length) {
dismissPump.start() dismissPump.start();
} }
} }
function dismissNotification(wrapper) { function dismissNotification(wrapper) {
if (!wrapper || !wrapper.notification) { if (!wrapper || !wrapper.notification) {
return return;
} }
wrapper.popup = false wrapper.popup = false;
wrapper.notification.dismiss() wrapper.notification.dismiss();
} }
function disablePopups(disable) { function disablePopups(disable) {
popupsDisabled = disable popupsDisabled = disable;
if (disable) { if (disable) {
notificationQueue = [] notificationQueue = [];
for (const notif of visibleNotifications) { for (const notif of visibleNotifications) {
notif.popup = false notif.popup = false;
} }
visibleNotifications = [] visibleNotifications = [];
} }
} }
function processQueue() { function processQueue() {
if (addGateBusy) { if (addGateBusy) {
return return;
} }
if (popupsDisabled) { if (popupsDisabled) {
return return;
} }
if (SessionData.doNotDisturb) { if (SessionData.doNotDisturb) {
return return;
} }
if (notificationQueue.length === 0) { if (notificationQueue.length === 0) {
return return;
} }
const activePopupCount = visibleNotifications.filter(n => n && n.popup).length const activePopupCount = visibleNotifications.filter(n => n && n.popup).length;
if (activePopupCount >= 4) { if (activePopupCount >= 4) {
return return;
} }
const next = notificationQueue.shift() const next = notificationQueue.shift();
next.seq = ++seqCounter next.seq = ++seqCounter;
visibleNotifications = [...visibleNotifications, next] visibleNotifications = [...visibleNotifications, next];
next.popup = true next.popup = true;
if (next.timer.interval > 0) { if (next.timer.interval > 0) {
next.timer.start() next.timer.start();
} }
addGateBusy = true addGateBusy = true;
addGate.restart() addGate.restart();
} }
function removeFromVisibleNotifications(wrapper) { function removeFromVisibleNotifications(wrapper) {
visibleNotifications = visibleNotifications.filter(n => n !== wrapper) visibleNotifications = visibleNotifications.filter(n => n !== wrapper);
processQueue() processQueue();
} }
function releaseWrapper(w) { function releaseWrapper(w) {
visibleNotifications = visibleNotifications.filter(n => n !== w) visibleNotifications = visibleNotifications.filter(n => n !== w);
notificationQueue = notificationQueue.filter(n => n !== w) notificationQueue = notificationQueue.filter(n => n !== w);
if (w && w.destroy && !w.isPersistent && notifications.indexOf(w) === -1) { if (w && w.destroy && !w.isPersistent && notifications.indexOf(w) === -1) {
Qt.callLater(() => { Qt.callLater(() => {
try { try {
w.destroy() w.destroy();
} catch (e) { } catch (e) {}
});
}
})
} }
} }
function getGroupKey(wrapper) { function getGroupKey(wrapper) {
if (wrapper.desktopEntry && wrapper.desktopEntry !== "") { if (wrapper.desktopEntry && wrapper.desktopEntry !== "") {
return wrapper.desktopEntry.toLowerCase() return wrapper.desktopEntry.toLowerCase();
} }
return wrapper.appName.toLowerCase() return wrapper.appName.toLowerCase();
} }
function _recomputeGroups() { function _recomputeGroups() {
if (_suspendGrouping) { if (_suspendGrouping) {
_groupsDirty = true _groupsDirty = true;
return return;
} }
_groupCache = { _groupCache = {
"notifications": _calcGroupedNotifications(), "notifications": _calcGroupedNotifications(),
"popups": _calcGroupedPopups() "popups": _calcGroupedPopups()
} };
_groupsDirty = false _groupsDirty = false;
} }
function _recomputeGroupsLater() { function _recomputeGroupsLater() {
_groupsDirty = true _groupsDirty = true;
if (!groupsDebounce.running) { if (!groupsDebounce.running) {
groupsDebounce.start() groupsDebounce.start();
} }
} }
function _calcGroupedNotifications() { function _calcGroupedNotifications() {
const groups = {} const groups = {};
for (const notif of notifications) { for (const notif of notifications) {
if (!notif) continue if (!notif)
const groupKey = getGroupKey(notif) continue;
const groupKey = getGroupKey(notif);
if (!groups[groupKey]) { if (!groups[groupKey]) {
groups[groupKey] = { groups[groupKey] = {
"key": groupKey, "key": groupKey,
@@ -562,34 +553,35 @@ Singleton {
"latestNotification": null, "latestNotification": null,
"count": 0, "count": 0,
"hasInlineReply": false "hasInlineReply": false
} };
} }
groups[groupKey].notifications.unshift(notif) groups[groupKey].notifications.unshift(notif);
groups[groupKey].latestNotification = groups[groupKey].notifications[0] groups[groupKey].latestNotification = groups[groupKey].notifications[0];
groups[groupKey].count = groups[groupKey].notifications.length groups[groupKey].count = groups[groupKey].notifications.length;
if (notif.notification.hasInlineReply) { if (notif.notification.hasInlineReply) {
groups[groupKey].hasInlineReply = true groups[groupKey].hasInlineReply = true;
} }
} }
return Object.values(groups).sort((a, b) => { return Object.values(groups).sort((a, b) => {
const aUrgency = a.latestNotification.urgency || NotificationUrgency.Low const aUrgency = a.latestNotification.urgency || NotificationUrgency.Low;
const bUrgency = b.latestNotification.urgency || NotificationUrgency.Low const bUrgency = b.latestNotification.urgency || NotificationUrgency.Low;
if (aUrgency !== bUrgency) { if (aUrgency !== bUrgency) {
return bUrgency - aUrgency return bUrgency - aUrgency;
} }
return b.latestNotification.time.getTime() - a.latestNotification.time.getTime() return b.latestNotification.time.getTime() - a.latestNotification.time.getTime();
}) });
} }
function _calcGroupedPopups() { function _calcGroupedPopups() {
const groups = {} const groups = {};
for (const notif of popups) { for (const notif of popups) {
if (!notif) continue if (!notif)
const groupKey = getGroupKey(notif) continue;
const groupKey = getGroupKey(notif);
if (!groups[groupKey]) { if (!groups[groupKey]) {
groups[groupKey] = { groups[groupKey] = {
"key": groupKey, "key": groupKey,
@@ -598,92 +590,92 @@ Singleton {
"latestNotification": null, "latestNotification": null,
"count": 0, "count": 0,
"hasInlineReply": false "hasInlineReply": false
} };
} }
groups[groupKey].notifications.unshift(notif) groups[groupKey].notifications.unshift(notif);
groups[groupKey].latestNotification = groups[groupKey].notifications[0] groups[groupKey].latestNotification = groups[groupKey].notifications[0];
groups[groupKey].count = groups[groupKey].notifications.length groups[groupKey].count = groups[groupKey].notifications.length;
if (notif.notification.hasInlineReply) { if (notif.notification.hasInlineReply) {
groups[groupKey].hasInlineReply = true groups[groupKey].hasInlineReply = true;
} }
} }
return Object.values(groups).sort((a, b) => { return Object.values(groups).sort((a, b) => {
return b.latestNotification.time.getTime() - a.latestNotification.time.getTime() return b.latestNotification.time.getTime() - a.latestNotification.time.getTime();
}) });
} }
function toggleGroupExpansion(groupKey) { function toggleGroupExpansion(groupKey) {
let newExpandedGroups = {} let newExpandedGroups = {};
for (const key in expandedGroups) { for (const key in expandedGroups) {
newExpandedGroups[key] = expandedGroups[key] newExpandedGroups[key] = expandedGroups[key];
} }
newExpandedGroups[groupKey] = !newExpandedGroups[groupKey] newExpandedGroups[groupKey] = !newExpandedGroups[groupKey];
expandedGroups = newExpandedGroups expandedGroups = newExpandedGroups;
} }
function dismissGroup(groupKey) { function dismissGroup(groupKey) {
const group = groupedNotifications.find(g => g.key === groupKey) const group = groupedNotifications.find(g => g.key === groupKey);
if (group) { if (group) {
for (const notif of group.notifications) { for (const notif of group.notifications) {
if (notif && notif.notification) { if (notif && notif.notification) {
notif.notification.dismiss() notif.notification.dismiss();
} }
} }
} else { } else {
for (const notif of allWrappers) { for (const notif of allWrappers) {
if (notif && notif.notification && getGroupKey(notif) === groupKey) { if (notif && notif.notification && getGroupKey(notif) === groupKey) {
notif.notification.dismiss() notif.notification.dismiss();
} }
} }
} }
} }
function clearGroupExpansionState(groupKey) { function clearGroupExpansionState(groupKey) {
let newExpandedGroups = {} let newExpandedGroups = {};
for (const key in expandedGroups) { for (const key in expandedGroups) {
if (key !== groupKey && expandedGroups[key]) { if (key !== groupKey && expandedGroups[key]) {
newExpandedGroups[key] = true newExpandedGroups[key] = true;
} }
} }
expandedGroups = newExpandedGroups expandedGroups = newExpandedGroups;
} }
function cleanupExpansionStates() { function cleanupExpansionStates() {
const currentGroupKeys = new Set(groupedNotifications.map(g => g.key)) const currentGroupKeys = new Set(groupedNotifications.map(g => g.key));
const currentMessageIds = new Set() const currentMessageIds = new Set();
for (const group of groupedNotifications) { for (const group of groupedNotifications) {
for (const notif of group.notifications) { for (const notif of group.notifications) {
if (notif && notif.notification) { if (notif && notif.notification) {
currentMessageIds.add(notif.notification.id) currentMessageIds.add(notif.notification.id);
} }
} }
} }
let newExpandedGroups = {} let newExpandedGroups = {};
for (const key in expandedGroups) { for (const key in expandedGroups) {
if (currentGroupKeys.has(key) && expandedGroups[key]) { if (currentGroupKeys.has(key) && expandedGroups[key]) {
newExpandedGroups[key] = true newExpandedGroups[key] = true;
} }
} }
expandedGroups = newExpandedGroups expandedGroups = newExpandedGroups;
let newExpandedMessages = {} let newExpandedMessages = {};
for (const messageId in expandedMessages) { for (const messageId in expandedMessages) {
if (currentMessageIds.has(messageId) && expandedMessages[messageId]) { if (currentMessageIds.has(messageId) && expandedMessages[messageId]) {
newExpandedMessages[messageId] = true newExpandedMessages[messageId] = true;
} }
} }
expandedMessages = newExpandedMessages expandedMessages = newExpandedMessages;
} }
function toggleMessageExpansion(messageId) { function toggleMessageExpansion(messageId) {
let newExpandedMessages = {} let newExpandedMessages = {};
for (const key in expandedMessages) { for (const key in expandedMessages) {
newExpandedMessages[key] = expandedMessages[key] newExpandedMessages[key] = expandedMessages[key];
} }
newExpandedMessages[messageId] = !newExpandedMessages[messageId] newExpandedMessages[messageId] = !newExpandedMessages[messageId];
expandedMessages = newExpandedMessages expandedMessages = newExpandedMessages;
} }
Connections { Connections {
@@ -692,13 +684,13 @@ Singleton {
if (SessionData.doNotDisturb) { if (SessionData.doNotDisturb) {
// Hide all current popups when DND is enabled // Hide all current popups when DND is enabled
for (const notif of visibleNotifications) { for (const notif of visibleNotifications) {
notif.popup = false notif.popup = false;
} }
visibleNotifications = [] visibleNotifications = [];
notificationQueue = [] notificationQueue = [];
} else { } else {
// Re-enable popup processing when DND is disabled // Re-enable popup processing when DND is disabled
processQueue() processQueue();
} }
} }
} }
@@ -706,7 +698,7 @@ Singleton {
Connections { Connections {
target: typeof SettingsData !== "undefined" ? SettingsData : null target: typeof SettingsData !== "undefined" ? SettingsData : null
function onUse24HourClockChanged() { function onUse24HourClockChanged() {
root.clockFormatChanged = !root.clockFormatChanged root.clockFormatChanged = !root.clockFormatChanged;
} }
} }
} }

View File

@@ -16,6 +16,7 @@ Singleton {
property var systemUpdatePopout: null property var systemUpdatePopout: null
property var settingsModal: null property var settingsModal: null
property var settingsModalLoader: null
property var clipboardHistoryModal: null property var clipboardHistoryModal: null
property var spotlightModal: null property var spotlightModal: null
property var powerMenuModal: null property var powerMenuModal: null
@@ -191,14 +192,50 @@ Singleton {
} }
} }
property bool _settingsWantsOpen: false
property bool _settingsWantsToggle: false
function openSettings() { function openSettings() {
settingsModal?.show(); if (settingsModal) {
settingsModal.show();
} else if (settingsModalLoader) {
_settingsWantsOpen = true;
_settingsWantsToggle = false;
settingsModalLoader.activeAsync = true;
}
} }
function closeSettings() { function closeSettings() {
settingsModal?.close(); settingsModal?.close();
} }
function toggleSettings() {
if (settingsModal) {
settingsModal.toggle();
} else if (settingsModalLoader) {
_settingsWantsToggle = true;
_settingsWantsOpen = false;
settingsModalLoader.activeAsync = true;
}
}
function unloadSettings() {
if (settingsModalLoader) {
settingsModal = null;
settingsModalLoader.active = false;
}
}
function _onSettingsModalLoaded() {
if (_settingsWantsOpen) {
_settingsWantsOpen = false;
settingsModal?.show();
} else if (_settingsWantsToggle) {
_settingsWantsToggle = false;
settingsModal?.toggle();
}
}
function openClipboardHistory() { function openClipboardHistory() {
clipboardHistoryModal?.show(); clipboardHistoryModal?.show();
} }