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

Compare commits

...

35 Commits

Author SHA1 Message Date
bbedward 02a274ebe2 fix(launcher): select first file search result by default fixes #1967 2026-03-11 12:47:39 -04:00
nick-linux8 fc7b61c20b Issue:(Settings)Switched Neovim Mutagen Theme To Default False (#1964)
* Issue:(Settings)Switched Neovim Mutagen Theme To Default False

* also set to false in settingsData
- this is the case when file fails to parse

---------

Co-authored-by: bbedward <bbedward@gmail.com>
2026-03-11 12:44:14 -04:00
bbedward 5880043f56 fix: dsearch references 2026-03-11 12:08:24 -04:00
Triệu Kha fee3b7f2a7 fix(appdrawer): launcher launched via appdrawer doesnt respect size (#1960)
setting
2026-03-11 10:56:21 -04:00
bbedward c0b0339fca plugins: fix list delegates 2026-03-11 09:58:24 -04:00
bbedward 26c1e62204 fix(dankbar): use ID as tie breaker 2026-03-10 11:48:33 -04:00
purian23 7b2d4dbe30 dankinstall: Update Arch/Quickshell installation 2026-03-10 11:05:25 -04:00
CaptainSpof 78c5d46c6b fix(wallpaper): follow symlinks when scanning wallpaper directory (#1947) 2026-03-10 11:05:25 -04:00
purian23 3fb85df504 fix(Clipboard) remove unused copyServe logic 2026-03-10 11:05:00 -04:00
micko 227dd24726 update deprecated syntax (#1928) 2026-03-10 11:05:00 -04:00
purian23 ae6a656899 fix(Clipboard): Epic RAM Growth - Closes #1920 2026-03-10 11:05:00 -04:00
Connor Welsh a4055e0f01 fix(Calendar): add missing qs.Common import (#1926)
fixes calendar events getting dropped
2026-03-10 11:05:00 -04:00
Lucas 6d98c229ef flake: allow extra QT packages in dms-shell package (#1903) 2026-03-10 11:04:01 -04:00
Michael Erdely 71d93ad85e Not everyone uses paru or yay on Arch: Support pacman command (#1900)
* Not everyone uses paru or yay on Arch: Support pacman command
* Handle sudo properly when using pacman
* Move pacman to bottom per Purian23
* Remote duplicate which -- thanks Purian23!
2026-03-10 11:04:01 -04:00
Triệu Kha 4ec21fcd3d fix(dock): Dock flickering when having cursor floating by the side (#1897) 2026-03-10 11:04:01 -04:00
Lucas 0a2fe03fee ipc: update DankBar selection (#1894)
* ipc: update DankBar selection

* ipc: use getPreferredBar in dash open

* ipc: don't toggle dash on dash open
2026-03-10 11:04:01 -04:00
Triệu Kha 4f4745609b fix(osd): play/pause icon flipped in MediaPlaybackOSD (#1889) 2026-03-10 11:03:23 -04:00
purian23 a69cd515fb fix(dbar): Fixes autohide + click through edge case 2026-03-10 11:03:23 -04:00
purian23 06c4b97a6b fix(notifications): Allow duplicate history entry management w/unique IDs & source tracking 2026-03-10 11:03:23 -04:00
purian23 a6cf71a190 fix(notifications): Apply appIdSubs to iconFrImage fallback path - Consistent with the appIcon PR changes in #1880. 2026-03-10 11:01:34 -04:00
odt 21750156dc refactor(icons): centralize icon resolution into Paths.resolveIconPath/resolveIconUrl (#1880)
Supersedes #1878. Rather than duplicating the moddedAppId + file path
substitution pattern inline across 8 files, this introduces two
centralized functions in Paths.qml:

- resolveIconPath(iconName): for Quickshell.iconPath() callsites,
  with DesktopService.resolveIconPath() fallback
- resolveIconUrl(iconName): for image://icon/ URL callsites

All consumer files now use one-line calls. When no substitutions are
configured, moddedAppId() returns the original name unchanged (zero
cost), so this has no impact on users who don't use the feature.

Affected components:
- AppIconRenderer (8 lines → 1)
- NotificationCard, NotificationPopup, HistoryNotificationCard
- DockContextMenu, AppsDockContextMenu
- LauncherContent, LauncherTab (×3)

Co-authored-by: odtgit <odtgit@taliops.com>
2026-03-10 11:00:57 -04:00
supposede f9b737f543 Update toolbar button styles with primary color (#1879) 2026-03-10 10:55:58 -04:00
odt 246b59f3b9 fix(icons): apply file path substitutions in launcher icon resolution (#1877)
Follow-up to #1867. The launcher's AppIconRenderer used its own
Quickshell.iconPath() call without going through appIdSubstitutions,
so PWA icons configured via regex file path rules were not resolved
in the app launcher.

Co-authored-by: odtgit <odtgit@taliops.com>
2026-03-10 10:55:53 -04:00
bbedward dcda81ea64 wallpaper: bump render settle timer 2026-03-01 10:27:10 -05:00
bbedward 9909b665cd blurred wallpaper: defer update disabling much longer 2026-02-28 15:40:18 -05:00
bbedward 4bcd786be3 wallpaper: defer updatesEnabled binding 2026-02-28 01:10:26 -05:00
bbedward 64c9222000 loginctl: add fallbacks for session discovery 2026-02-27 10:12:25 -05:00
Iris 12acf2dd51 Change IsPluggedIn logic (#1859)
Co-authored-by: Iris <iris@raidev.eu>
2026-02-27 10:12:22 -05:00
Jan Greimann fea97b4aad Adjust SystemUpdate process (#1845)
This fixes the problem that the system update terminal closes when the package manager encounters a problem (exit code != 0), allowing the user to understand the problem.

Signed-off-by: Jan Phillip Greimann <jan.greimann@ionos.com>
2026-02-27 10:12:19 -05:00
Kangheng Liu c6d398eeac Systray: call context menu fallback for legacy protocol (#1839)
* systray: add call contextmenu fallback

directly call dbus contextmenu method. needs refactoring to be more
robust.

* add TODO

---------

Co-authored-by: bbedward <bbedward@gmail.com>
2026-02-27 10:12:16 -05:00
bbedward 7a74be83d7 greeter: sync power menu options 2026-02-25 14:50:47 -05:00
bbedward 67a6427418 dankdash: fix menu overlays 2026-02-25 14:50:47 -05:00
purian23 18b20d3225 feat: Add independent power action confirmation settings for dms greeter 2026-02-25 14:50:47 -05:00
bbedward 8a76885fb6 desktop widgets: fix deactive loaders when widgets disabled fixes #1813 2026-02-25 12:34:47 -05:00
bbedward 69b1e61ab7 stage 1.4.3 2026-02-25 12:34:43 -05:00
57 changed files with 760 additions and 297 deletions
+11 -1
View File
@@ -222,16 +222,19 @@ func init() {
func runClipCopy(cmd *cobra.Command, args []string) { func runClipCopy(cmd *cobra.Command, args []string) {
var data []byte var data []byte
copyFromStdin := false
switch { switch {
case len(args) > 0: case len(args) > 0:
data = []byte(args[0]) data = []byte(args[0])
default: case clipCopyDownload || clipCopyType == "__multi__":
var err error var err error
data, err = io.ReadAll(os.Stdin) data, err = io.ReadAll(os.Stdin)
if err != nil { if err != nil {
log.Fatalf("read stdin: %v", err) log.Fatalf("read stdin: %v", err)
} }
default:
copyFromStdin = true
} }
if clipCopyDownload { if clipCopyDownload {
@@ -257,6 +260,13 @@ func runClipCopy(cmd *cobra.Command, args []string) {
return return
} }
if copyFromStdin {
if err := clipboard.CopyReader(os.Stdin, clipCopyType, clipCopyForeground, clipCopyPasteOnce); err != nil {
log.Fatalf("copy: %v", err)
}
return
}
if err := clipboard.CopyOpts(data, clipCopyType, clipCopyForeground, clipCopyPasteOnce); err != nil { if err := clipboard.CopyOpts(data, clipCopyType, clipCopyForeground, clipCopyPasteOnce); err != nil {
log.Fatalf("copy: %v", err) log.Fatalf("copy: %v", err)
} }
+92 -8
View File
@@ -1,10 +1,12 @@
package clipboard package clipboard
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"syscall" "syscall"
"github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_data_control" "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_data_control"
@@ -12,17 +14,37 @@ import (
) )
func Copy(data []byte, mimeType string) error { func Copy(data []byte, mimeType string) error {
return CopyOpts(data, mimeType, false, false) return CopyReader(bytes.NewReader(data), mimeType, false, false)
} }
func CopyOpts(data []byte, mimeType string, foreground, pasteOnce bool) error { func CopyOpts(data []byte, mimeType string, foreground, pasteOnce bool) error {
if foreground {
return copyServeWithWriter(func(writer io.Writer) error {
total := 0
for total < len(data) {
n, err := writer.Write(data[total:])
total += n
if err != nil {
return err
}
}
if total != len(data) {
return io.ErrShortWrite
}
return nil
}, mimeType, pasteOnce)
}
return CopyReader(bytes.NewReader(data), mimeType, foreground, pasteOnce)
}
func CopyReader(data io.Reader, mimeType string, foreground, pasteOnce bool) error {
if !foreground { if !foreground {
return copyFork(data, mimeType, pasteOnce) return copyFork(data, mimeType, pasteOnce)
} }
return copyServe(data, mimeType, pasteOnce) return copyServeReader(data, mimeType, pasteOnce)
} }
func copyFork(data []byte, mimeType string, pasteOnce bool) error { func copyFork(data io.Reader, mimeType string, pasteOnce bool) error {
args := []string{os.Args[0], "cl", "copy", "--foreground"} args := []string{os.Args[0], "cl", "copy", "--foreground"}
if pasteOnce { if pasteOnce {
args = append(args, "--paste-once") args = append(args, "--paste-once")
@@ -30,11 +52,15 @@ func copyFork(data []byte, mimeType string, pasteOnce bool) error {
args = append(args, "--type", mimeType) args = append(args, "--type", mimeType)
cmd := exec.Command(args[0], args[1:]...) cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin = nil
cmd.Stdout = nil cmd.Stdout = nil
cmd.Stderr = nil cmd.Stderr = nil
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
if stdinSource, ok := data.(*os.File); ok {
cmd.Stdin = stdinSource
return cmd.Start()
}
stdin, err := cmd.StdinPipe() stdin, err := cmd.StdinPipe()
if err != nil { if err != nil {
return fmt.Errorf("stdin pipe: %w", err) return fmt.Errorf("stdin pipe: %w", err)
@@ -44,16 +70,66 @@ func copyFork(data []byte, mimeType string, pasteOnce bool) error {
return fmt.Errorf("start: %w", err) return fmt.Errorf("start: %w", err)
} }
if _, err := stdin.Write(data); err != nil { if _, err := io.Copy(stdin, data); err != nil {
stdin.Close() stdin.Close()
return fmt.Errorf("write stdin: %w", err) return fmt.Errorf("write stdin: %w", err)
} }
stdin.Close() if err := stdin.Close(); err != nil {
return fmt.Errorf("close stdin: %w", err)
}
return nil return nil
} }
func copyServe(data []byte, mimeType string, pasteOnce bool) error { func copyServeReader(data io.Reader, mimeType string, pasteOnce bool) error {
cachedData, err := createClipboardCacheFile()
if err != nil {
return fmt.Errorf("create clipboard cache file: %w", err)
}
defer os.Remove(cachedData.Name())
if _, err := io.Copy(cachedData, data); err != nil {
return fmt.Errorf("cache clipboard data: %w", err)
}
if err := cachedData.Close(); err != nil {
return fmt.Errorf("close temp cache file: %w", err)
}
return copyServeWithWriter(func(writer io.Writer) error {
cachedFile, err := os.Open(cachedData.Name())
if err != nil {
return fmt.Errorf("open temp cache file: %w", err)
}
defer cachedFile.Close()
if _, err := io.Copy(writer, cachedFile); err != nil {
return fmt.Errorf("write clipboard data: %w", err)
}
return nil
}, mimeType, pasteOnce)
}
func createClipboardCacheFile() (*os.File, error) {
preferredDirs := []string{}
if cacheDir, err := os.UserCacheDir(); err == nil {
preferredDirs = append(preferredDirs, filepath.Join(cacheDir, "dms", "clipboard"))
}
preferredDirs = append(preferredDirs, "/var/tmp/dms/clipboard")
for _, dir := range preferredDirs {
if err := os.MkdirAll(dir, 0o700); err != nil {
continue
}
cachedData, err := os.CreateTemp(dir, "dms-clipboard-*")
if err == nil {
return cachedData, nil
}
}
return os.CreateTemp("", "dms-clipboard-*")
}
func copyServeWithWriter(writeTo func(io.Writer) error, mimeType string, pasteOnce bool) error {
display, err := wlclient.Connect("") display, err := wlclient.Connect("")
if err != nil { if err != nil {
return fmt.Errorf("wayland connect: %w", err) return fmt.Errorf("wayland connect: %w", err)
@@ -139,12 +215,18 @@ func copyServe(data []byte, mimeType string, pasteOnce bool) error {
cancelled := make(chan struct{}) cancelled := make(chan struct{})
pasted := make(chan struct{}, 1) pasted := make(chan struct{}, 1)
sendErr := make(chan error, 1)
source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) { source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) {
defer syscall.Close(e.Fd) defer syscall.Close(e.Fd)
file := os.NewFile(uintptr(e.Fd), "pipe") file := os.NewFile(uintptr(e.Fd), "pipe")
defer file.Close() defer file.Close()
file.Write(data) if err := writeTo(file); err != nil {
select {
case sendErr <- err:
default:
}
}
select { select {
case pasted <- struct{}{}: case pasted <- struct{}{}:
default: default:
@@ -165,6 +247,8 @@ func copyServe(data []byte, mimeType string, pasteOnce bool) error {
select { select {
case <-cancelled: case <-cancelled:
return nil return nil
case err := <-sendErr:
return err
case <-pasted: case <-pasted:
if pasteOnce { if pasteOnce {
return nil return nil
+18 -39
View File
@@ -440,29 +440,10 @@ func (a *ArchDistribution) installAURPackages(ctx context.Context, packages []st
a.log(fmt.Sprintf("Installing AUR packages manually: %s", strings.Join(packages, ", "))) a.log(fmt.Sprintf("Installing AUR packages manually: %s", strings.Join(packages, ", ")))
hasNiri := false hasNiri := false
hasQuickshell := false
for _, pkg := range packages { for _, pkg := range packages {
if pkg == "niri-git" { if pkg == "niri-git" {
hasNiri = true hasNiri = true
} }
if pkg == "quickshell" || pkg == "quickshell-git" {
hasQuickshell = true
}
}
// If quickshell is in the list, always reinstall google-breakpad first
if hasQuickshell {
progressChan <- InstallProgressMsg{
Phase: PhaseAURPackages,
Progress: 0.63,
Step: "Reinstalling google-breakpad for quickshell...",
IsComplete: false,
CommandInfo: "Reinstalling prerequisite AUR package for quickshell",
}
if err := a.installSingleAURPackage(ctx, "google-breakpad", sudoPassword, progressChan, 0.63, 0.65); err != nil {
return fmt.Errorf("failed to reinstall google-breakpad prerequisite for quickshell: %w", err)
}
} }
// If niri is in the list, install makepkg-git-lfs-proto first if not already installed // If niri is in the list, install makepkg-git-lfs-proto first if not already installed
@@ -616,10 +597,16 @@ func (a *ArchDistribution) installSingleAURPackage(ctx context.Context, pkg, sud
return fmt.Errorf("failed to remove optdepends from .SRCINFO for %s: %w", pkg, err) return fmt.Errorf("failed to remove optdepends from .SRCINFO for %s: %w", pkg, err)
} }
// Skip dependency installation for dms-shell-git and dms-shell-bin srcinfoPath = filepath.Join(packageDir, ".SRCINFO")
// since we manually manage those dependencies if pkg == "dms-shell-bin" {
if pkg != "dms-shell-git" && pkg != "dms-shell-bin" { progressChan <- InstallProgressMsg{
// Pre-install dependencies from .SRCINFO Phase: PhaseAURPackages,
Progress: startProgress + 0.35*(endProgress-startProgress),
Step: fmt.Sprintf("Skipping dependency installation for %s (manually managed)...", pkg),
IsComplete: false,
LogOutput: fmt.Sprintf("Dependencies for %s are installed separately", pkg),
}
} else {
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseAURPackages, Phase: PhaseAURPackages,
Progress: startProgress + 0.3*(endProgress-startProgress), Progress: startProgress + 0.3*(endProgress-startProgress),
@@ -628,19 +615,19 @@ func (a *ArchDistribution) installSingleAURPackage(ctx context.Context, pkg, sud
CommandInfo: "Installing package dependencies and makedepends", CommandInfo: "Installing package dependencies and makedepends",
} }
// Install dependencies and makedepends explicitly // Install dependencies from .SRCINFO
srcinfoPath = filepath.Join(packageDir, ".SRCINFO") depFilter := ""
if pkg == "dms-shell-git" {
depFilter = ` | sed -E 's/[[:space:]]*(quickshell|dgop)[[:space:]]*/ /g' | tr -s ' '`
}
depsCmd := exec.CommandContext(ctx, "bash", "-c", depsCmd := exec.CommandContext(ctx, "bash", "-c",
fmt.Sprintf(` fmt.Sprintf(`
deps=$(grep "depends = " "%s" | grep -v "makedepends" | sed 's/.*depends = //' | tr '\n' ' ' | sed 's/[[:space:]]*$//') deps=$(grep "depends = " "%s" | grep -v "makedepends" | sed 's/.*depends = //' | tr '\n' ' ' %s | sed 's/[[:space:]]*$//')
if [[ "%s" == *"quickshell"* ]]; then
deps=$(echo "$deps" | sed 's/google-breakpad//g' | sed 's/ / /g' | sed 's/^ *//g' | sed 's/ *$//g')
fi
if [ ! -z "$deps" ] && [ "$deps" != " " ]; then if [ ! -z "$deps" ] && [ "$deps" != " " ]; then
echo '%s' | sudo -S pacman -S --needed --noconfirm $deps echo '%s' | sudo -S pacman -S --needed --noconfirm $deps
fi fi
`, srcinfoPath, pkg, sudoPassword)) `, srcinfoPath, depFilter, sudoPassword))
if err := a.runWithProgress(depsCmd, progressChan, PhaseAURPackages, startProgress+0.3*(endProgress-startProgress), startProgress+0.35*(endProgress-startProgress)); err != nil { if err := a.runWithProgress(depsCmd, progressChan, PhaseAURPackages, startProgress+0.3*(endProgress-startProgress), startProgress+0.35*(endProgress-startProgress)); err != nil {
return fmt.Errorf("FAILED to install runtime dependencies for %s: %w", pkg, err) return fmt.Errorf("FAILED to install runtime dependencies for %s: %w", pkg, err)
@@ -657,14 +644,6 @@ func (a *ArchDistribution) installSingleAURPackage(ctx context.Context, pkg, sud
if err := a.runWithProgress(makedepsCmd, progressChan, PhaseAURPackages, startProgress+0.35*(endProgress-startProgress), startProgress+0.4*(endProgress-startProgress)); err != nil { if err := a.runWithProgress(makedepsCmd, progressChan, PhaseAURPackages, startProgress+0.35*(endProgress-startProgress), startProgress+0.4*(endProgress-startProgress)); err != nil {
return fmt.Errorf("FAILED to install make dependencies for %s: %w", pkg, err) return fmt.Errorf("FAILED to install make dependencies for %s: %w", pkg, err)
} }
} else {
progressChan <- InstallProgressMsg{
Phase: PhaseAURPackages,
Progress: startProgress + 0.35*(endProgress-startProgress),
Step: fmt.Sprintf("Skipping dependency installation for %s (manually managed)...", pkg),
IsComplete: false,
LogOutput: fmt.Sprintf("Dependencies for %s are installed separately", pkg),
}
} }
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
@@ -677,7 +656,7 @@ func (a *ArchDistribution) installSingleAURPackage(ctx context.Context, pkg, sud
buildCmd := exec.CommandContext(ctx, "makepkg", "--noconfirm") buildCmd := exec.CommandContext(ctx, "makepkg", "--noconfirm")
buildCmd.Dir = packageDir buildCmd.Dir = packageDir
buildCmd.Env = append(os.Environ(), "PKGEXT=.pkg.tar") // Disable compression for speed buildCmd.Env = append(os.Environ(), "PKGEXT=.pkg.tar")
if err := a.runWithProgress(buildCmd, progressChan, PhaseAURPackages, startProgress+0.4*(endProgress-startProgress), startProgress+0.7*(endProgress-startProgress)); err != nil { if err := a.runWithProgress(buildCmd, progressChan, PhaseAURPackages, startProgress+0.4*(endProgress-startProgress), startProgress+0.7*(endProgress-startProgress)); err != nil {
return fmt.Errorf("failed to build %s: %w", pkg, err) return fmt.Errorf("failed to build %s: %w", pkg, err)
@@ -5,5 +5,6 @@ const (
dbusPath = "/org/freedesktop/login1" dbusPath = "/org/freedesktop/login1"
dbusManagerInterface = "org.freedesktop.login1.Manager" dbusManagerInterface = "org.freedesktop.login1.Manager"
dbusSessionInterface = "org.freedesktop.login1.Session" dbusSessionInterface = "org.freedesktop.login1.Session"
dbusUserInterface = "org.freedesktop.login1.User"
dbusPropsInterface = "org.freedesktop.DBus.Properties" dbusPropsInterface = "org.freedesktop.DBus.Properties"
) )
+198 -9
View File
@@ -17,15 +17,8 @@ func NewManager() (*Manager, error) {
return nil, fmt.Errorf("failed to connect to system bus: %w", err) return nil, fmt.Errorf("failed to connect to system bus: %w", err)
} }
sessionID := os.Getenv("XDG_SESSION_ID")
if sessionID == "" {
sessionID = "self"
}
m := &Manager{ m := &Manager{
state: &SessionState{ state: &SessionState{},
SessionID: sessionID,
},
stateMutex: sync.RWMutex{}, stateMutex: sync.RWMutex{},
stopChan: make(chan struct{}), stopChan: make(chan struct{}),
@@ -60,12 +53,13 @@ func (m *Manager) initialize() error {
m.initializeFallbackDelay() m.initializeFallbackDelay()
sessionPath, err := m.getSession(m.state.SessionID) sessionID, sessionPath, err := m.discoverSession()
if err != nil { if err != nil {
return fmt.Errorf("failed to get session path: %w", err) return fmt.Errorf("failed to get session path: %w", err)
} }
m.stateMutex.Lock() m.stateMutex.Lock()
m.state.SessionID = sessionID
m.state.SessionPath = string(sessionPath) m.state.SessionPath = string(sessionPath)
m.sessionPath = sessionPath m.sessionPath = sessionPath
m.stateMutex.Unlock() m.stateMutex.Unlock()
@@ -79,6 +73,41 @@ func (m *Manager) initialize() error {
return nil return nil
} }
func (m *Manager) discoverSession() (string, dbus.ObjectPath, error) {
// 1. Explicit XDG_SESSION_ID
if id := os.Getenv("XDG_SESSION_ID"); id != "" {
if path, err := m.getSession(id); err == nil {
fmt.Fprintf(os.Stderr, "loginctl: using XDG_SESSION_ID=%s\n", id)
return id, path, nil
}
}
// 2. PID-based lookup (works when caller is inside a session cgroup)
if id, path, err := m.getSessionByPID(uint32(os.Getpid())); err == nil {
fmt.Fprintf(os.Stderr, "loginctl: found session %s via PID\n", id)
return id, path, nil
}
// 3. User's primary display session (handles UWSM and similar)
if id, path, err := m.getUserDisplaySession(); err == nil {
fmt.Fprintf(os.Stderr, "loginctl: found session %s via User.Display\n", id)
return id, path, nil
}
// 4. Score all sessions for current UID
if id, path, err := m.findBestSession(); err == nil {
fmt.Fprintf(os.Stderr, "loginctl: found session %s via ListSessions scoring\n", id)
return id, path, nil
}
// 5. Last resort: "self"
path, err := m.getSession("self")
if err != nil {
return "", "", fmt.Errorf("%w", err)
}
return "self", path, nil
}
func (m *Manager) getSession(id string) (dbus.ObjectPath, error) { func (m *Manager) getSession(id string) (dbus.ObjectPath, error) {
var out dbus.ObjectPath var out dbus.ObjectPath
err := m.managerObj.Call(dbusManagerInterface+".GetSession", 0, id).Store(&out) err := m.managerObj.Call(dbusManagerInterface+".GetSession", 0, id).Store(&out)
@@ -88,6 +117,166 @@ func (m *Manager) getSession(id string) (dbus.ObjectPath, error) {
return out, nil return out, nil
} }
func (m *Manager) getSessionByPID(pid uint32) (string, dbus.ObjectPath, error) {
var path dbus.ObjectPath
if err := m.managerObj.Call(dbusManagerInterface+".GetSessionByPID", 0, pid).Store(&path); err != nil {
return "", "", err
}
sessionObj := m.conn.Object(dbusDest, path)
var id dbus.Variant
if err := sessionObj.Call(dbusPropsInterface+".Get", 0, dbusSessionInterface, "Id").Store(&id); err != nil {
return "", "", err
}
return id.Value().(string), path, nil
}
func (m *Manager) getUserDisplaySession() (string, dbus.ObjectPath, error) {
uid := uint32(os.Getuid())
var userPath dbus.ObjectPath
if err := m.managerObj.Call(dbusManagerInterface+".GetUser", 0, uid).Store(&userPath); err != nil {
return "", "", err
}
userObj := m.conn.Object(dbusDest, userPath)
var display dbus.Variant
if err := userObj.Call(dbusPropsInterface+".Get", 0, dbusUserInterface, "Display").Store(&display); err != nil {
return "", "", err
}
pair, ok := display.Value().([]any)
if !ok || len(pair) < 2 {
return "", "", fmt.Errorf("unexpected Display format")
}
sessionID, _ := pair[0].(string)
sessionPath, _ := pair[1].(dbus.ObjectPath)
if sessionID == "" || sessionPath == "" {
return "", "", fmt.Errorf("empty Display session")
}
return sessionID, sessionPath, nil
}
type sessionCandidate struct {
id string
path dbus.ObjectPath
}
func (m *Manager) findBestSession() (string, dbus.ObjectPath, error) {
// ListSessions returns a(susso): [][]any where each entry is [id, uid, name, seat, path]
var raw [][]any
if err := m.managerObj.Call(dbusManagerInterface+".ListSessions", 0).Store(&raw); err != nil {
return "", "", err
}
uid := uint32(os.Getuid())
var candidates []sessionCandidate
for _, entry := range raw {
if len(entry) < 5 {
continue
}
entryUID, _ := entry[1].(uint32)
if entryUID != uid {
continue
}
id, _ := entry[0].(string)
path, _ := entry[4].(dbus.ObjectPath)
if id != "" && path != "" {
candidates = append(candidates, sessionCandidate{id: id, path: path})
}
}
if len(candidates) == 0 {
return "", "", fmt.Errorf("no sessions for uid %d", uid)
}
bestScore := -1
var best sessionCandidate
for _, c := range candidates {
score := m.scoreSession(c.path)
if score > bestScore {
bestScore = score
best = c
}
}
if bestScore < 0 {
return "", "", fmt.Errorf("no viable session found")
}
return best.id, best.path, nil
}
func (m *Manager) scoreSession(path dbus.ObjectPath) int {
obj := m.conn.Object(dbusDest, path)
var props map[string]dbus.Variant
if err := obj.Call(dbusPropsInterface+".GetAll", 0, dbusSessionInterface).Store(&props); err != nil {
return -1
}
getStr := func(key string) string {
if v, ok := props[key]; ok {
if s, ok := v.Value().(string); ok {
return s
}
}
return ""
}
getBool := func(key string) bool {
if v, ok := props[key]; ok {
if b, ok := v.Value().(bool); ok {
return b
}
}
return false
}
getUint32 := func(key string) uint32 {
if v, ok := props[key]; ok {
if u, ok := v.Value().(uint32); ok {
return u
}
}
return 0
}
class := getStr("Class")
if class != "user" {
return -1
}
if getBool("Remote") {
return -1
}
score := 0
if getBool("Active") {
score += 100
}
switch getStr("Type") {
case "wayland", "x11":
score += 80
case "tty":
score += 10
}
if v, ok := props["Seat"]; ok {
if seatArr, ok := v.Value().([]any); ok && len(seatArr) >= 1 {
if seat, ok := seatArr[0].(string); ok && seat != "" {
score += 40
if seat == "seat0" {
score += 10
}
}
}
}
if getUint32("VTNr") > 0 {
score += 20
}
return score
}
func (m *Manager) refreshSessionBinding() error { func (m *Manager) refreshSessionBinding() error {
if m.managerObj == nil || m.conn == nil { if m.managerObj == nil || m.conn == nil {
return fmt.Errorf("manager not fully initialized") return fmt.Errorf("manager not fully initialized")
+62 -56
View File
@@ -72,76 +72,82 @@
"${cleanVersion}${dateSuffix}${revSuffix}"; "${cleanVersion}${dateSuffix}${revSuffix}";
in in
{ {
dms-shell = pkgs.buildGoModule ( dms-shell = pkgs.lib.makeOverridable (
let
rootSrc = ./.;
in
{ {
inherit version; extraQtPackages ? [ ],
pname = "dms-shell"; }:
src = ./core; pkgs.buildGoModule (
vendorHash = "sha256-dEk7IOd6aQwaxZruxQclN7TGMyb8EJOl6NBWRsoZ9HQ="; let
rootSrc = ./.;
qtPackages = (qmlPkgs pkgs) ++ extraQtPackages;
in
{
inherit version;
pname = "dms-shell";
src = ./core;
vendorHash = "sha256-dEk7IOd6aQwaxZruxQclN7TGMyb8EJOl6NBWRsoZ9HQ=";
subPackages = [ "cmd/dms" ]; subPackages = [ "cmd/dms" ];
ldflags = [ ldflags = [
"-s" "-s"
"-w" "-w"
"-X 'main.Version=${version}'" "-X 'main.Version=${version}'"
]; ];
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
installShellFiles installShellFiles
makeWrapper makeWrapper
]; ];
postInstall = '' postInstall = ''
mkdir -p $out/share/quickshell/dms mkdir -p $out/share/quickshell/dms
cp -r ${rootSrc}/quickshell/. $out/share/quickshell/dms/ cp -r ${rootSrc}/quickshell/. $out/share/quickshell/dms/
chmod u+w $out/share/quickshell/dms/VERSION chmod u+w $out/share/quickshell/dms/VERSION
echo "${version}" > $out/share/quickshell/dms/VERSION echo "${version}" > $out/share/quickshell/dms/VERSION
# Install desktop file and icon # Install desktop file and icon
install -D ${rootSrc}/assets/dms-open.desktop \ install -D ${rootSrc}/assets/dms-open.desktop \
$out/share/applications/dms-open.desktop $out/share/applications/dms-open.desktop
install -D ${rootSrc}/core/assets/danklogo.svg \ install -D ${rootSrc}/core/assets/danklogo.svg \
$out/share/hicolor/scalable/apps/danklogo.svg $out/share/hicolor/scalable/apps/danklogo.svg
wrapProgram $out/bin/dms \ wrapProgram $out/bin/dms \
--add-flags "-c $out/share/quickshell/dms" \ --add-flags "-c $out/share/quickshell/dms" \
--prefix "NIXPKGS_QT6_QML_IMPORT_PATH" ":" "${mkQmlImportPath pkgs (qmlPkgs pkgs)}" \ --prefix "NIXPKGS_QT6_QML_IMPORT_PATH" ":" "${mkQmlImportPath pkgs qtPackages}" \
--prefix "QT_PLUGIN_PATH" ":" "${mkQtPluginPath pkgs (qmlPkgs pkgs)}" --prefix "QT_PLUGIN_PATH" ":" "${mkQtPluginPath pkgs qtPackages}"
install -Dm644 ${rootSrc}/assets/systemd/dms.service \ install -Dm644 ${rootSrc}/assets/systemd/dms.service \
$out/lib/systemd/user/dms.service $out/lib/systemd/user/dms.service
substituteInPlace $out/lib/systemd/user/dms.service \ substituteInPlace $out/lib/systemd/user/dms.service \
--replace-fail /usr/bin/dms $out/bin/dms \ --replace-fail /usr/bin/dms $out/bin/dms \
--replace-fail /usr/bin/pkill ${pkgs.procps}/bin/pkill --replace-fail /usr/bin/pkill ${pkgs.procps}/bin/pkill
substituteInPlace $out/share/quickshell/dms/Modules/Greetd/assets/dms-greeter \ substituteInPlace $out/share/quickshell/dms/Modules/Greetd/assets/dms-greeter \
--replace-fail /bin/bash ${pkgs.bashInteractive}/bin/bash --replace-fail /bin/bash ${pkgs.bashInteractive}/bin/bash
substituteInPlace $out/share/quickshell/dms/assets/pam/fprint \ substituteInPlace $out/share/quickshell/dms/assets/pam/fprint \
--replace-fail pam_fprintd.so ${pkgs.fprintd}/lib/security/pam_fprintd.so --replace-fail pam_fprintd.so ${pkgs.fprintd}/lib/security/pam_fprintd.so
installShellCompletion --cmd dms \ installShellCompletion --cmd dms \
--bash <($out/bin/dms completion bash) \ --bash <($out/bin/dms completion bash) \
--fish <($out/bin/dms completion fish) \ --fish <($out/bin/dms completion fish) \
--zsh <($out/bin/dms completion zsh) --zsh <($out/bin/dms completion zsh)
''; '';
meta = { meta = {
description = "Desktop shell for wayland compositors built with Quickshell & GO"; description = "Desktop shell for wayland compositors built with Quickshell & GO";
homepage = "https://danklinux.com"; homepage = "https://danklinux.com";
changelog = "https://github.com/AvengeMedia/DankMaterialShell/releases/tag/v${version}"; changelog = "https://github.com/AvengeMedia/DankMaterialShell/releases/tag/v${version}";
license = pkgs.lib.licenses.mit; license = pkgs.lib.licenses.mit;
mainProgram = "dms"; mainProgram = "dms";
platforms = pkgs.lib.platforms.linux; platforms = pkgs.lib.platforms.linux;
}; };
} }
); )
) { };
quickshell = quickshell.packages.${system}.default; quickshell = quickshell.packages.${system}.default;
+28 -3
View File
@@ -71,15 +71,40 @@ Singleton {
return appId; return appId;
} }
function resolveIconPath(iconName: string): string {
if (!iconName) return "";
const moddedId = moddedAppId(iconName);
if (moddedId !== iconName) {
if (moddedId.startsWith("~") || moddedId.startsWith("/"))
return toFileUrl(expandTilde(moddedId));
if (moddedId.startsWith("file://"))
return moddedId;
return Quickshell.iconPath(moddedId, true);
}
return Quickshell.iconPath(iconName, true) || DesktopService.resolveIconPath(iconName);
}
function resolveIconUrl(iconName: string): string {
if (!iconName) return "";
const moddedId = moddedAppId(iconName);
if (moddedId !== iconName) {
if (moddedId.startsWith("~") || moddedId.startsWith("/"))
return toFileUrl(expandTilde(moddedId));
if (moddedId.startsWith("file://"))
return moddedId;
return "image://icon/" + moddedId;
}
return "image://icon/" + iconName;
}
function getAppIcon(appId: string, desktopEntry: var): string { function getAppIcon(appId: string, desktopEntry: var): string {
if (appId === "org.quickshell") { if (appId === "org.quickshell") {
return Qt.resolvedUrl("../assets/danklogo.svg"); return Qt.resolvedUrl("../assets/danklogo.svg");
} }
const moddedId = moddedAppId(appId); const moddedId = moddedAppId(appId);
if (moddedId !== appId) { if (moddedId !== appId)
return Quickshell.iconPath(moddedId, true); return resolveIconPath(appId);
}
if (desktopEntry && desktopEntry.icon) { if (desktopEntry && desktopEntry.icon) {
return Quickshell.iconPath(desktopEntry.icon, true); return Quickshell.iconPath(desktopEntry.icon, true);
+1 -1
View File
@@ -437,7 +437,7 @@ Singleton {
property bool matugenTemplateGhostty: true property bool matugenTemplateGhostty: true
property bool matugenTemplateKitty: true property bool matugenTemplateKitty: true
property bool matugenTemplateFoot: true property bool matugenTemplateFoot: true
property bool matugenTemplateNeovim: true property bool matugenTemplateNeovim: false
property bool matugenTemplateAlacritty: true property bool matugenTemplateAlacritty: true
property bool matugenTemplateWezterm: true property bool matugenTemplateWezterm: true
property bool matugenTemplateDgop: true property bool matugenTemplateDgop: true
+1 -1
View File
@@ -262,7 +262,7 @@ var SPEC = {
matugenTemplateKitty: { def: true }, matugenTemplateKitty: { def: true },
matugenTemplateFoot: { def: true }, matugenTemplateFoot: { def: true },
matugenTemplateAlacritty: { def: true }, matugenTemplateAlacritty: { def: true },
matugenTemplateNeovim: { def: true }, matugenTemplateNeovim: { def: false },
matugenTemplateWezterm: { def: true }, matugenTemplateWezterm: { def: true },
matugenTemplateDgop: { def: true }, matugenTemplateDgop: { def: true },
matugenTemplateKcolorscheme: { def: true }, matugenTemplateKcolorscheme: { def: true },
+5 -5
View File
@@ -154,18 +154,18 @@ Item {
property string _barLayoutStateJson: { property string _barLayoutStateJson: {
const configs = SettingsData.barConfigs; const configs = SettingsData.barConfigs;
const mapped = configs.map(c => ({ const mapped = configs.map((c, i) => ({
id: c.id, id: c.id,
position: c.position, position: c.position,
autoHide: c.autoHide, autoHide: c.autoHide,
visible: c.visible visible: c.visible,
_origIndex: i
})).sort((a, b) => { })).sort((a, b) => {
const aVertical = a.position === SettingsData.Position.Left || a.position === SettingsData.Position.Right; const aVertical = a.position === SettingsData.Position.Left || a.position === SettingsData.Position.Right;
const bVertical = b.position === SettingsData.Position.Left || b.position === SettingsData.Position.Right; const bVertical = b.position === SettingsData.Position.Left || b.position === SettingsData.Position.Right;
if (aVertical !== bVertical) { if (aVertical !== bVertical)
return aVertical - bVertical; return aVertical - bVertical;
} return a._origIndex - b._origIndex;
return String(a.id).localeCompare(String(b.id));
}); });
return JSON.stringify(mapped); return JSON.stringify(mapped);
} }
+79 -31
View File
@@ -21,11 +21,37 @@ Item {
required property var workspaceRenameModalLoader required property var workspaceRenameModalLoader
required property var windowRuleModalLoader required property var windowRuleModalLoader
function getFirstBar() { function getPreferredBar(refPropertyName) {
if (!root.dankBarRepeater || root.dankBarRepeater.count === 0) if (!root.dankBarRepeater || root.dankBarRepeater.count === 0)
return null; return null;
const firstLoader = root.dankBarRepeater.itemAt(0);
return firstLoader ? firstLoader.item : null; const focusedScreenName = BarWidgetService.getFocusedScreenName();
const loaders = Array.from({
length: root.dankBarRepeater.count
}, (_, i) => root.dankBarRepeater.itemAt(i));
let currentBar = null;
for (const loader of loaders) {
const instances = loader?.item?.barVariants?.instances || [];
for (const bar of instances) {
if (!bar)
continue;
const onFocusedScreen = focusedScreenName && bar.modelData?.name === focusedScreenName;
const hasRef = !refPropertyName || !!bar[refPropertyName];
if (hasRef) {
currentBar = bar;
if (onFocusedScreen)
break;
}
}
}
return currentBar;
} }
IpcHandler { IpcHandler {
@@ -97,9 +123,9 @@ Item {
IpcHandler { IpcHandler {
function open(): string { function open(): string {
const bar = root.getFirstBar(); const bar = root.getPreferredBar("controlCenterButtonRef");
if (bar) { if (bar) {
bar.triggerControlCenterOnFocusedScreen(); bar.triggerControlCenter();
return "CONTROL_CENTER_OPEN_SUCCESS"; return "CONTROL_CENTER_OPEN_SUCCESS";
} }
return "CONTROL_CENTER_OPEN_FAILED"; return "CONTROL_CENTER_OPEN_FAILED";
@@ -114,9 +140,14 @@ Item {
} }
function toggle(): string { function toggle(): string {
const bar = root.getFirstBar(); if (root.controlCenterLoader.item?.shouldBeVisible) {
root.controlCenterLoader.item.close();
return "CONTROL_CENTER_TOGGLE_SUCCESS";
}
const bar = root.getPreferredBar("controlCenterButtonRef");
if (bar) { if (bar) {
bar.triggerControlCenterOnFocusedScreen(); bar.triggerControlCenter();
return "CONTROL_CENTER_TOGGLE_SUCCESS"; return "CONTROL_CENTER_TOGGLE_SUCCESS";
} }
return "CONTROL_CENTER_TOGGLE_FAILED"; return "CONTROL_CENTER_TOGGLE_FAILED";
@@ -131,27 +162,37 @@ Item {
IpcHandler { IpcHandler {
function open(tab: string): string { function open(tab: string): string {
root.dankDashPopoutLoader.active = true; const bar = root.getPreferredBar("clockButtonRef");
if (root.dankDashPopoutLoader.item) { if (!bar)
switch (tab.toLowerCase()) { return "DASH_OPEN_FAILED";
case "media":
root.dankDashPopoutLoader.item.currentTabIndex = 1; const dash = root.dankDashPopoutLoader.item;
break; const onSameScreen = dash && dash.shouldBeVisible && dash.triggerScreen?.name === bar.screen?.name;
case "wallpaper":
root.dankDashPopoutLoader.item.currentTabIndex = 2; if (!onSameScreen) {
break; bar.triggerWallpaperBrowser();
case "weather":
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 3 : 0;
break;
default:
root.dankDashPopoutLoader.item.currentTabIndex = 0;
break;
}
root.dankDashPopoutLoader.item.setTriggerPosition(Screen.width / 2, Theme.barHeight + Theme.spacingS, 100, "center", Screen);
root.dankDashPopoutLoader.item.dashVisible = true;
return "DASH_OPEN_SUCCESS";
} }
return "DASH_OPEN_FAILED";
if (!root.dankDashPopoutLoader.item)
return "DASH_OPEN_FAILED";
switch (tab.toLowerCase()) {
case "media":
root.dankDashPopoutLoader.item.currentTabIndex = 1;
break;
case "wallpaper":
root.dankDashPopoutLoader.item.currentTabIndex = 2;
break;
case "weather":
root.dankDashPopoutLoader.item.currentTabIndex = SettingsData.weatherEnabled ? 3 : 0;
break;
default:
root.dankDashPopoutLoader.item.currentTabIndex = 0;
break;
}
root.dankDashPopoutLoader.item.dashVisible = true;
return "DASH_OPEN_SUCCESS";
} }
function close(): string { function close(): string {
@@ -163,8 +204,14 @@ Item {
} }
function toggle(tab: string): string { function toggle(tab: string): string {
const bar = root.getFirstBar(); if (root.dankDashPopoutLoader.item?.dashVisible) {
if (bar && bar.triggerWallpaperBrowserOnFocusedScreen()) { root.dankDashPopoutLoader.item.dashVisible = false;
return "DASH_TOGGLE_SUCCESS";
}
const bar = root.getPreferredBar("clockButtonRef");
if (bar) {
bar.triggerWallpaperBrowser();
if (root.dankDashPopoutLoader.item) { if (root.dankDashPopoutLoader.item) {
switch (tab.toLowerCase()) { switch (tab.toLowerCase()) {
case "media": case "media":
@@ -521,8 +568,9 @@ Item {
IpcHandler { IpcHandler {
function wallpaper(): string { function wallpaper(): string {
const bar = root.getFirstBar(); const bar = root.getPreferredBar("clockButtonRef");
if (bar && bar.triggerWallpaperBrowserOnFocusedScreen()) { if (bar) {
bar.triggerWallpaperBrowser();
return "SUCCESS: Toggled wallpaper browser"; return "SUCCESS: Toggled wallpaper browser";
} }
return "ERROR: Failed to toggle wallpaper browser"; return "ERROR: Failed to toggle wallpaper browser";
@@ -875,9 +875,7 @@ Item {
_applyHighlights(newSections, searchQuery); _applyHighlights(newSections, searchQuery);
flatModel = Scorer.flattenSections(newSections); flatModel = Scorer.flattenSections(newSections);
sections = newSections; sections = newSections;
if (selectedFlatIndex >= flatModel.length) { selectedFlatIndex = getFirstItemIndex();
selectedFlatIndex = getFirstItemIndex();
}
updateSelectedItem(); updateSelectedItem();
}); });
} }
@@ -643,7 +643,7 @@ FocusScope {
Image { Image {
width: 40 width: 40
height: 40 height: 40
source: editingApp?.icon ? "image://icon/" + editingApp.icon : "image://icon/application-x-executable" source: Paths.resolveIconUrl(editingApp?.icon || "application-x-executable")
sourceSize.width: 40 sourceSize.width: 40
sourceSize.height: 40 sourceSize.height: 40
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
@@ -460,7 +460,7 @@ Item {
switch (mode) { switch (mode) {
case "files": case "files":
if (!DSearchService.dsearchAvailable) if (!DSearchService.dsearchAvailable)
return I18n.tr("File search requires dsearch\nInstall from github.com/morelazers/dsearch"); return I18n.tr("File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch");
if (!hasQuery) if (!hasQuery)
return I18n.tr("Type to search files"); return I18n.tr("Type to search files");
if (root.controller.searchQuery.length < 2) if (root.controller.searchQuery.length < 2)
@@ -8,6 +8,9 @@ DankPopout {
layerNamespace: "dms:app-launcher" layerNamespace: "dms:app-launcher"
readonly property real screenWidth: screen?.width ?? 1920
readonly property real screenHeight: screen?.height ?? 1080
property string _pendingMode: "" property string _pendingMode: ""
property string _pendingQuery: "" property string _pendingQuery: ""
@@ -41,8 +44,35 @@ DankPopout {
openWithQuery(query); openWithQuery(query);
} }
popupWidth: 560 readonly property int _baseWidth: {
popupHeight: 640 switch (SettingsData.dankLauncherV2Size) {
case "micro":
return 500;
case "medium":
return 720;
case "large":
return 860;
default:
return 620;
}
}
readonly property int _baseHeight: {
switch (SettingsData.dankLauncherV2Size) {
case "micro":
return 480;
case "medium":
return 720;
case "large":
return 860;
default:
return 600;
}
}
popupWidth: Math.min(_baseWidth, screenWidth - 100)
popupHeight: Math.min(_baseHeight, screenHeight - 100)
triggerWidth: 40 triggerWidth: 40
positioning: "" positioning: ""
contentHandlesKeys: contentLoader.item?.launcherContent?.editMode ?? false contentHandlesKeys: contentLoader.item?.launcherContent?.editMode ?? false
@@ -87,10 +87,6 @@ Variants {
Component.onCompleted: { Component.onCompleted: {
if (typeof blurWallpaperWindow.updatesEnabled !== "undefined") if (typeof blurWallpaperWindow.updatesEnabled !== "undefined")
blurWallpaperWindow.updatesEnabled = Qt.binding(() => root.effectActive || root._renderSettling || currentWallpaper.status === Image.Loading || nextWallpaper.status === Image.Loading); blurWallpaperWindow.updatesEnabled = Qt.binding(() => root.effectActive || root._renderSettling || currentWallpaper.status === Image.Loading || nextWallpaper.status === Image.Loading);
if (!source) {
root._renderSettling = false;
}
isInitialized = true; isInitialized = true;
} }
@@ -113,7 +109,7 @@ Variants {
Timer { Timer {
id: renderSettleTimer id: renderSettleTimer
interval: 100 interval: 1000
onTriggered: root._renderSettling = false onTriggered: root._renderSettling = false
} }
+14 -6
View File
@@ -552,8 +552,9 @@ PanelWindow {
readonly property var _leftSection: topBarContent ? (barWindow.isVertical ? topBarContent.vLeftSection : topBarContent.hLeftSection) : null readonly property var _leftSection: topBarContent ? (barWindow.isVertical ? topBarContent.vLeftSection : topBarContent.hLeftSection) : null
readonly property var _centerSection: topBarContent ? (barWindow.isVertical ? topBarContent.vCenterSection : topBarContent.hCenterSection) : null readonly property var _centerSection: topBarContent ? (barWindow.isVertical ? topBarContent.vCenterSection : topBarContent.hCenterSection) : null
readonly property var _rightSection: topBarContent ? (barWindow.isVertical ? topBarContent.vRightSection : topBarContent.hRightSection) : null readonly property var _rightSection: topBarContent ? (barWindow.isVertical ? topBarContent.vRightSection : topBarContent.hRightSection) : null
readonly property real _revealProgress: topBarSlide.x + topBarSlide.y
function sectionRect(section, isCenter) { function sectionRect(section, isCenter, _dep) {
if (!section) if (!section)
return { return {
"x": 0, "x": 0,
@@ -582,7 +583,7 @@ PanelWindow {
item: clickThroughEnabled ? null : inputMask item: clickThroughEnabled ? null : inputMask
Region { Region {
readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._leftSection, false) : { readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._leftSection, false, barWindow._revealProgress) : {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 0, "w": 0,
@@ -595,7 +596,7 @@ PanelWindow {
} }
Region { Region {
readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._centerSection, true) : { readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._centerSection, true, barWindow._revealProgress) : {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 0, "w": 0,
@@ -608,7 +609,7 @@ PanelWindow {
} }
Region { Region {
readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._rightSection, false) : { readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._rightSection, false, barWindow._revealProgress) : {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 0, "w": 0,
@@ -619,6 +620,14 @@ PanelWindow {
width: r.w width: r.w
height: r.h height: r.h
} }
Region {
readonly property bool active: barWindow.clickThroughEnabled && !inputMask.showing
x: active ? inputMask.x : 0
y: active ? inputMask.y : 0
width: active ? inputMask.width : 0
height: active ? inputMask.height : 0
}
} }
Item { Item {
@@ -631,7 +640,7 @@ PanelWindow {
Timer { Timer {
id: revealHold id: revealHold
interval: barConfig?.autoHideDelay ?? 250 interval: barWindow.clickThroughEnabled ? Math.max((barConfig?.autoHideDelay ?? 250) * 6, 1500) : (barConfig?.autoHideDelay ?? 250)
repeat: false repeat: false
onTriggered: { onTriggered: {
if (!topBarMouseArea.containsMouse && !topBarCore.hasActivePopout) { if (!topBarMouseArea.containsMouse && !topBarCore.hasActivePopout) {
@@ -689,7 +698,6 @@ PanelWindow {
Connections { Connections {
function onBarConfigChanged() { function onBarConfigChanged() {
topBarCore.autoHide = barConfig?.autoHide ?? false; topBarCore.autoHide = barConfig?.autoHide ?? false;
revealHold.interval = barConfig?.autoHideDelay ?? 250;
} }
target: rootWindow target: rootWindow
@@ -273,7 +273,7 @@ PanelWindow {
IconImage { IconImage {
anchors.fill: parent anchors.fill: parent
source: modelData.icon ? Quickshell.iconPath(modelData.icon, true) : "" source: modelData.icon ? Paths.resolveIconPath(modelData.icon) : ""
smooth: true smooth: true
asynchronous: true asynchronous: true
visible: status === Image.Ready visible: status === Image.Ready
@@ -41,6 +41,12 @@ BasePill {
return `${id}::${tooltipTitle}`; return `${id}::${tooltipTitle}`;
} }
// ! TODO - replace with either native dbus client (like plugins use) or just a DMS cli or something
function callContextMenuFallback(trayItemId, globalX, globalY) {
const script = ['ITEMS=$(dbus-send --session --print-reply --dest=org.kde.StatusNotifierWatcher /StatusNotifierWatcher org.freedesktop.DBus.Properties.Get string:org.kde.StatusNotifierWatcher string:RegisteredStatusNotifierItems 2>/dev/null)', 'while IFS= read -r line; do', ' line="${line#*\\\"}"', ' line="${line%\\\"*}"', ' [ -z "$line" ] && continue', ' BUS="${line%%/*}"', ' OBJ="/${line#*/}"', ' ID=$(dbus-send --session --print-reply --dest="$BUS" "$OBJ" org.freedesktop.DBus.Properties.Get string:org.kde.StatusNotifierItem string:Id 2>/dev/null | grep -oP "(?<=\\\")(.*?)(?=\\\")" | tail -1)', ' if [ "$ID" = "$1" ]; then', ' dbus-send --session --type=method_call --dest="$BUS" "$OBJ" org.kde.StatusNotifierItem.ContextMenu int32:"$2" int32:"$3"', ' exit 0', ' fi', 'done <<< "$ITEMS"',].join("\n");
Quickshell.execDetached(["bash", "-c", script, "_", trayItemId, String(globalX), String(globalY)]);
}
property int _trayOrderTrigger: 0 property int _trayOrderTrigger: 0
Connections { Connections {
@@ -380,8 +386,11 @@ BasePill {
return; return;
if (mouse.button !== Qt.RightButton) if (mouse.button !== Qt.RightButton)
return; return;
if (!delegateRoot.trayItem?.hasMenu) if (!delegateRoot.trayItem?.hasMenu) {
const gp = trayItemArea.mapToGlobal(mouse.x, mouse.y);
root.callContextMenuFallback(delegateRoot.trayItem.id, Math.round(gp.x), Math.round(gp.y));
return; return;
}
root.menuOpen = false; root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
} }
@@ -637,8 +646,11 @@ BasePill {
return; return;
if (mouse.button !== Qt.RightButton) if (mouse.button !== Qt.RightButton)
return; return;
if (!delegateRoot.trayItem?.hasMenu) if (!delegateRoot.trayItem?.hasMenu) {
const gp = trayItemArea.mapToGlobal(mouse.x, mouse.y);
root.callContextMenuFallback(delegateRoot.trayItem.id, Math.round(gp.x), Math.round(gp.y));
return; return;
}
root.menuOpen = false; root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
} }
@@ -1065,9 +1077,11 @@ BasePill {
root.menuOpen = false; root.menuOpen = false;
return; return;
} }
if (!trayItem.hasMenu) {
if (!trayItem.hasMenu) const gp = itemArea.mapToGlobal(mouse.x, mouse.y);
root.callContextMenuFallback(trayItem.id, Math.round(gp.x), Math.round(gp.y));
return; return;
}
root.showForTrayItem(trayItem, menuContainer, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); root.showForTrayItem(trayItem, menuContainer, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis);
} }
} }
+3 -5
View File
@@ -447,9 +447,8 @@ Variants {
height: { height: {
if (dock.isVertical) { if (dock.isVertical) {
if (!dock.reveal) // Keep the taller hit area regardless of the reveal state to prevent shrinking loop
return Math.min(Math.max(dockBackground.height + 64, 200), screenHeight * 0.5); return Math.min(Math.max(dockBackground.height + 64, 200), screenHeight * 0.5);
return Math.min(dockBackground.height + 8 + dock.borderThickness, maxDockHeight);
} }
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1; return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
} }
@@ -457,8 +456,7 @@ Variants {
if (dock.isVertical) { if (dock.isVertical) {
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1; return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
} }
if (!dock.reveal) // Keep the wider hit area regardless of the reveal state to prevent shrinking loop
return Math.min(Math.max(dockBackground.width + 64, 200), screenWidth * 0.5);
return Math.min(dockBackground.width + 8 + dock.borderThickness, maxDockWidth); return Math.min(dockBackground.width + 8 + dock.borderThickness, maxDockWidth);
} }
anchors { anchors {
+1 -1
View File
@@ -329,7 +329,7 @@ PanelWindow {
IconImage { IconImage {
anchors.fill: parent anchors.fill: parent
source: modelData.icon ? Quickshell.iconPath(modelData.icon, true) : "" source: modelData.icon ? Paths.resolveIconPath(modelData.icon) : ""
smooth: true smooth: true
asynchronous: true asynchronous: true
visible: status === Image.Ready visible: status === Image.Ready
@@ -41,6 +41,11 @@ Singleton {
property string lockDateFormat: "" property string lockDateFormat: ""
property bool lockScreenShowPowerActions: true property bool lockScreenShowPowerActions: true
property bool lockScreenShowProfileImage: true property bool lockScreenShowProfileImage: true
property bool powerActionConfirm: true
property real powerActionHoldDuration: 0.5
property var powerMenuActions: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"]
property string powerMenuDefaultAction: "logout"
property bool powerMenuGridLayout: false
property var screenPreferences: ({}) property var screenPreferences: ({})
property int animationSpeed: 2 property int animationSpeed: 2
property string wallpaperFillMode: "Fill" property string wallpaperFillMode: "Fill"
@@ -75,6 +80,11 @@ Singleton {
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : ""; lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : "";
lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true; lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true;
lockScreenShowProfileImage = settings.lockScreenShowProfileImage !== undefined ? settings.lockScreenShowProfileImage : true; lockScreenShowProfileImage = settings.lockScreenShowProfileImage !== undefined ? settings.lockScreenShowProfileImage : true;
powerActionConfirm = settings.powerActionConfirm !== undefined ? settings.powerActionConfirm : true;
powerActionHoldDuration = settings.powerActionHoldDuration !== undefined ? settings.powerActionHoldDuration : 0.5;
powerMenuActions = settings.powerMenuActions !== undefined ? settings.powerMenuActions : ["reboot", "logout", "poweroff", "lock", "suspend", "restart"];
powerMenuDefaultAction = settings.powerMenuDefaultAction !== undefined ? settings.powerMenuDefaultAction : "logout";
powerMenuGridLayout = settings.powerMenuGridLayout !== undefined ? settings.powerMenuGridLayout : false;
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({}); screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({});
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : 2; animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : 2;
wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill"; wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill";
@@ -1231,6 +1231,12 @@ Item {
LockPowerMenu { LockPowerMenu {
id: powerMenu id: powerMenu
showLogout: false showLogout: false
powerActionConfirmOverride: GreetdSettings.powerActionConfirm
powerActionHoldDurationOverride: GreetdSettings.powerActionHoldDuration
powerMenuActionsOverride: GreetdSettings.powerMenuActions
powerMenuDefaultActionOverride: GreetdSettings.powerMenuDefaultAction
powerMenuGridLayoutOverride: GreetdSettings.powerMenuGridLayout
requiredActions: ["poweroff"]
onClosed: { onClosed: {
if (isPrimaryScreen && inputField && inputField.forceActiveFocus) { if (isPrimaryScreen && inputField && inputField.forceActiveFocus) {
Qt.callLater(() => inputField.forceActiveFocus()); Qt.callLater(() => inputField.forceActiveFocus());
+20 -7
View File
@@ -24,13 +24,20 @@ Rectangle {
property real holdProgress: 0 property real holdProgress: 0
property bool showHoldHint: false property bool showHoldHint: false
readonly property bool needsConfirmation: SettingsData.powerActionConfirm property var powerActionConfirmOverride: undefined
readonly property int holdDurationMs: SettingsData.powerActionHoldDuration * 1000 property var powerActionHoldDurationOverride: undefined
property var powerMenuActionsOverride: undefined
property var powerMenuDefaultActionOverride: undefined
property var powerMenuGridLayoutOverride: undefined
property var requiredActions: []
readonly property bool needsConfirmation: powerActionConfirmOverride !== undefined ? powerActionConfirmOverride : SettingsData.powerActionConfirm
readonly property int holdDurationMs: (powerActionHoldDurationOverride !== undefined ? powerActionHoldDurationOverride : SettingsData.powerActionHoldDuration) * 1000
signal closed signal closed
function updateVisibleActions() { function updateVisibleActions() {
const allActions = (typeof SettingsData !== "undefined" && SettingsData.powerMenuActions) ? SettingsData.powerMenuActions : ["logout", "suspend", "hibernate", "reboot", "poweroff"]; const allActions = powerMenuActionsOverride !== undefined ? powerMenuActionsOverride : ((typeof SettingsData !== "undefined" && SettingsData.powerMenuActions) ? SettingsData.powerMenuActions : ["logout", "suspend", "hibernate", "reboot", "poweroff"]);
const hibernateSupported = (typeof SessionService !== "undefined" && SessionService.hibernateSupported) || false; const hibernateSupported = (typeof SessionService !== "undefined" && SessionService.hibernateSupported) || false;
let filtered = allActions.filter(action => { let filtered = allActions.filter(action => {
if (action === "hibernate" && !hibernateSupported) if (action === "hibernate" && !hibernateSupported)
@@ -44,9 +51,14 @@ Rectangle {
return true; return true;
}); });
for (const action of requiredActions) {
if (!filtered.includes(action))
filtered.push(action);
}
visibleActions = filtered; visibleActions = filtered;
useGridLayout = (typeof SettingsData !== "undefined" && SettingsData.powerMenuGridLayout !== undefined) ? SettingsData.powerMenuGridLayout : false; useGridLayout = powerMenuGridLayoutOverride !== undefined ? powerMenuGridLayoutOverride : ((typeof SettingsData !== "undefined" && SettingsData.powerMenuGridLayout !== undefined) ? SettingsData.powerMenuGridLayout : false);
if (!useGridLayout) if (!useGridLayout)
return; return;
const count = visibleActions.length; const count = visibleActions.length;
@@ -73,7 +85,7 @@ Rectangle {
} }
function getDefaultActionIndex() { function getDefaultActionIndex() {
const defaultAction = (typeof SettingsData !== "undefined" && SettingsData.powerMenuDefaultAction) ? SettingsData.powerMenuDefaultAction : "suspend"; const defaultAction = powerMenuDefaultActionOverride !== undefined ? powerMenuDefaultActionOverride : ((typeof SettingsData !== "undefined" && SettingsData.powerMenuDefaultAction) ? SettingsData.powerMenuDefaultAction : "suspend");
const index = visibleActions.indexOf(defaultAction); const index = visibleActions.indexOf(defaultAction);
return index >= 0 ? index : 0; return index >= 0 ? index : 0;
} }
@@ -780,8 +792,9 @@ Rectangle {
} }
StyledText { StyledText {
readonly property real totalMs: SettingsData.powerActionHoldDuration * 1000 readonly property real totalMs: root.holdDurationMs
readonly property int remainingMs: Math.ceil(totalMs * (1 - root.holdProgress)) readonly property int remainingMs: Math.ceil(totalMs * (1 - root.holdProgress))
readonly property real durationSec: root.holdDurationMs / 1000
text: { text: {
if (root.showHoldHint) if (root.showHoldHint)
return I18n.tr("Hold longer to confirm"); return I18n.tr("Hold longer to confirm");
@@ -792,7 +805,7 @@ Rectangle {
} }
if (totalMs < 1000) if (totalMs < 1000)
return I18n.tr("Hold to confirm (%1 ms)").arg(totalMs); return I18n.tr("Hold to confirm (%1 ms)").arg(totalMs);
return I18n.tr("Hold to confirm (%1s)").arg(SettingsData.powerActionHoldDuration); return I18n.tr("Hold to confirm (%1s)").arg(durationSec);
} }
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: root.showHoldHint ? Theme.warning : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6) color: root.showHoldHint ? Theme.warning : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
@@ -122,12 +122,12 @@ Rectangle {
return ""; return "";
const appIcon = historyItem.appIcon; const appIcon = historyItem.appIcon;
if (!appIcon) if (!appIcon)
return iconFromImage ? "image://icon/" + iconFromImage : ""; return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/")) if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
return appIcon; return appIcon;
if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:")) if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
return ""; return "";
return Quickshell.iconPath(appIcon, true); return Paths.resolveIconPath(appIcon);
} }
hasImage: hasNotificationImage hasImage: hasNotificationImage
@@ -169,12 +169,12 @@ Rectangle {
return ""; return "";
const appIcon = notificationGroup?.latestNotification?.appIcon; const appIcon = notificationGroup?.latestNotification?.appIcon;
if (!appIcon) if (!appIcon)
return iconFromImage ? "image://icon/" + iconFromImage : ""; return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/")) if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
return appIcon; return appIcon;
if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:")) if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
return ""; return "";
return Quickshell.iconPath(appIcon, true); return Paths.resolveIconPath(appIcon);
} }
hasImage: hasNotificationImage hasImage: hasNotificationImage
@@ -503,12 +503,12 @@ Rectangle {
return ""; return "";
const appIcon = modelData?.appIcon; const appIcon = modelData?.appIcon;
if (!appIcon) if (!appIcon)
return iconFromImage ? "image://icon/" + iconFromImage : ""; return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/")) if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
return appIcon; return appIcon;
if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:")) if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
return ""; return "";
return Quickshell.iconPath(appIcon, true); return Paths.resolveIconPath(appIcon);
} }
fallbackIcon: { fallbackIcon: {
@@ -479,12 +479,12 @@ PanelWindow {
return ""; return "";
const appIcon = notificationData.appIcon; const appIcon = notificationData.appIcon;
if (!appIcon) if (!appIcon)
return iconFromImage ? "image://icon/" + iconFromImage : ""; return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/")) if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
return appIcon; return appIcon;
if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:")) if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
return ""; return "";
return Quickshell.iconPath(appIcon, true); return Paths.resolveIconPath(appIcon);
} }
hasImage: hasNotificationImage hasImage: hasNotificationImage
+2 -2
View File
@@ -27,11 +27,11 @@ DankOSD {
let icon = "music_note"; let icon = "music_note";
switch (player.playbackState) { switch (player.playbackState) {
case MprisPlaybackState.Playing: case MprisPlaybackState.Playing:
icon = "play_arrow"; icon = "pause";
break; break;
case MprisPlaybackState.Paused: case MprisPlaybackState.Paused:
case MprisPlaybackState.Stopped: case MprisPlaybackState.Stopped:
icon = "pause"; icon = "play_arrow";
break; break;
} }
if (icon === _displayIcon) if (icon === _displayIcon)
@@ -351,6 +351,7 @@ Item {
Loader { Loader {
id: contentLoader id: contentLoader
anchors.fill: parent anchors.fill: parent
active: root.widgetEnabled && root.activeComponent !== null
sourceComponent: root.activeComponent sourceComponent: root.activeComponent
function reloadComponent() { function reloadComponent() {
+3 -3
View File
@@ -897,7 +897,7 @@ Item {
Image { Image {
width: 24 width: 24
height: 24 height: 24
source: modelData.icon ? "image://icon/" + modelData.icon : "image://icon/application-x-executable" source: Paths.resolveIconUrl(modelData.icon || "application-x-executable")
sourceSize.width: 24 sourceSize.width: 24
sourceSize.height: 24 sourceSize.height: 24
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
@@ -1008,7 +1008,7 @@ Item {
Image { Image {
width: 24 width: 24
height: 24 height: 24
source: modelData.icon ? "image://icon/" + modelData.icon : "image://icon/application-x-executable" source: Paths.resolveIconUrl(modelData.icon || "application-x-executable")
sourceSize.width: 24 sourceSize.width: 24
sourceSize.height: 24 sourceSize.height: 24
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
@@ -1154,7 +1154,7 @@ Item {
Image { Image {
width: 24 width: 24
height: 24 height: 24
source: modelData.icon ? "image://icon/" + modelData.icon : "image://icon/application-x-executable" source: Paths.resolveIconUrl(modelData.icon || "application-x-executable")
sourceSize.width: 24 sourceSize.width: 24
sourceSize.height: 24 sourceSize.height: 24
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
@@ -408,6 +408,8 @@ FloatingWindow {
} }
clip: true clip: true
visible: !root.isLoading visible: !root.isLoading
add: null
displaced: null
ScrollBar.vertical: DankScrollbar { ScrollBar.vertical: DankScrollbar {
id: browserScrollbar id: browserScrollbar
+1 -1
View File
@@ -135,7 +135,7 @@ Variants {
Timer { Timer {
id: renderSettleTimer id: renderSettleTimer
interval: 100 interval: 1000
onTriggered: root._renderSettling = false onTriggered: root._renderSettling = false
} }
+2 -2
View File
@@ -56,8 +56,8 @@ Singleton {
} }
readonly property bool isCharging: batteryAvailable && batteries.some(b => b.state === UPowerDeviceState.Charging) readonly property bool isCharging: batteryAvailable && batteries.some(b => b.state === UPowerDeviceState.Charging)
// Is the system plugged in (none of the batteries are discharging or empty) // Is the system plugged in (Is not running on battery)
readonly property bool isPluggedIn: batteryAvailable && batteries.every(b => b.state !== UPowerDeviceState.Discharging) readonly property bool isPluggedIn: !UPower.onBattery
readonly property bool isLowBattery: batteryAvailable && batteryLevel <= 20 readonly property bool isLowBattery: batteryAvailable && batteryLevel <= 20
onIsPluggedInChanged: { onIsPluggedInChanged: {
+1
View File
@@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Common
Singleton { Singleton {
id: root id: root
+28 -5
View File
@@ -19,6 +19,7 @@ Singleton {
readonly property string historyFile: Paths.strip(Paths.cache) + "/notification_history.json" readonly property string historyFile: Paths.strip(Paths.cache) + "/notification_history.json"
readonly property string imageCacheDir: Paths.strip(Paths.cache) + "/notification_images" readonly property string imageCacheDir: Paths.strip(Paths.cache) + "/notification_images"
property bool historyLoaded: false property bool historyLoaded: false
property int historyEntryCounter: 0
property list<NotifWrapper> notificationQueue: [] property list<NotifWrapper> notificationQueue: []
property list<NotifWrapper> visibleNotifications: [] property list<NotifWrapper> visibleNotifications: []
@@ -73,6 +74,12 @@ Singleton {
onTriggered: root.performSaveHistory() onTriggered: root.performSaveHistory()
} }
function _makeHistoryEntryId(sourceId, timestamp) {
historyEntryCounter += 1;
const safeSource = sourceId && sourceId !== "" ? sourceId : "notification";
return safeSource + "_" + (timestamp || Date.now()) + "_" + historyEntryCounter;
}
function getImageCachePath(wrapper) { function getImageCachePath(wrapper) {
const ts = wrapper.time ? wrapper.time.getTime() : Date.now(); const ts = wrapper.time ? wrapper.time.getTime() : Date.now();
const id = wrapper.notification?.id?.toString() || "0"; const id = wrapper.notification?.id?.toString() || "0";
@@ -80,12 +87,13 @@ Singleton {
} }
function updateHistoryImage(wrapperId, imagePath) { function updateHistoryImage(wrapperId, imagePath) {
const idx = historyList.findIndex(n => n.id === wrapperId); const idx = historyList.findIndex(n => n.sourceNotificationId === wrapperId || n.id === wrapperId);
if (idx < 0) if (idx < 0)
return; return;
const item = historyList[idx]; const item = historyList[idx];
const updated = { const updated = {
id: item.id, id: item.id,
sourceNotificationId: item.sourceNotificationId || item.id,
summary: item.summary, summary: item.summary,
body: item.body, body: item.body,
htmlBody: item.htmlBody, htmlBody: item.htmlBody,
@@ -113,8 +121,11 @@ Singleton {
} else if (imageUrl && !imageUrl.startsWith("image://qsimage/")) { } else if (imageUrl && !imageUrl.startsWith("image://qsimage/")) {
persistableImage = imageUrl; persistableImage = imageUrl;
} }
const sourceNotificationId = wrapper.notification?.id?.toString() || "";
const timestamp = wrapper.time.getTime();
const data = { const data = {
id: wrapper.notification?.id?.toString() || Date.now().toString(), id: _makeHistoryEntryId(sourceNotificationId, timestamp),
sourceNotificationId: sourceNotificationId,
summary: wrapper.summary || "", summary: wrapper.summary || "",
body: wrapper.body || "", body: wrapper.body || "",
htmlBody: wrapper.htmlBody || wrapper.body || "", htmlBody: wrapper.htmlBody || wrapper.body || "",
@@ -122,7 +133,7 @@ Singleton {
appIcon: wrapper.appIcon || "", appIcon: wrapper.appIcon || "",
image: persistableImage, image: persistableImage,
urgency: urg, urgency: urg,
timestamp: wrapper.time.getTime(), timestamp: timestamp,
desktopEntry: wrapper.desktopEntry || "" desktopEntry: wrapper.desktopEntry || ""
}; };
let newList = [data, ...historyList]; let newList = [data, ...historyList];
@@ -152,6 +163,8 @@ Singleton {
const now = Date.now(); const now = Date.now();
const maxAgeMs = maxAgeDays > 0 ? maxAgeDays * 24 * 60 * 60 * 1000 : 0; const maxAgeMs = maxAgeDays > 0 ? maxAgeDays * 24 * 60 * 60 * 1000 : 0;
const loaded = []; const loaded = [];
const seenIds = {};
let needsRewrite = false;
for (const item of historyAdapter.notifications || []) { for (const item of historyAdapter.notifications || []) {
if (maxAgeMs > 0 && (now - item.timestamp) > maxAgeMs) if (maxAgeMs > 0 && (now - item.timestamp) > maxAgeMs)
@@ -162,8 +175,18 @@ Singleton {
if (htmlBody) { if (htmlBody) {
htmlBody = htmlBody.replace(/<img\b[^>]*>/gi, ""); htmlBody = htmlBody.replace(/<img\b[^>]*>/gi, "");
} }
const sourceNotificationId = (item.sourceNotificationId || item.id || "").toString();
let historyId = (item.id || "").toString();
if (!historyId || seenIds[historyId]) {
historyId = _makeHistoryEntryId(sourceNotificationId, item.timestamp || now);
needsRewrite = true;
}
if (!item.sourceNotificationId)
needsRewrite = true;
seenIds[historyId] = true;
loaded.push({ loaded.push({
id: item.id || "", id: historyId,
sourceNotificationId: sourceNotificationId,
summary: item.summary || "", summary: item.summary || "",
body: body, body: body,
htmlBody: htmlBody, htmlBody: htmlBody,
@@ -177,7 +200,7 @@ Singleton {
} }
historyList = loaded; historyList = loaded;
historyLoaded = true; historyLoaded = true;
if (maxAgeMs > 0 && loaded.length !== (historyAdapter.notifications || []).length) if ((maxAgeMs > 0 && loaded.length !== (historyAdapter.notifications || []).length) || needsRewrite)
saveHistory(); saveHistory();
} catch (e) { } catch (e) {
console.warn("NotificationService: load history failed:", e); console.warn("NotificationService: load history failed:", e);
+26 -23
View File
@@ -44,24 +44,26 @@ Singleton {
} }
} }
readonly property var archBasedPMSettings: { readonly property var archBasedPMSettings: function(requiresSudo) {
"listUpdatesSettings": { return {
"params": ["-Qu"], "listUpdatesSettings": {
"correctExitCodes": [0, 1] // Exit code 0 = updates available, 1 = no updates "params": ["-Qu"],
}, "correctExitCodes": [0, 1] // Exit code 0 = updates available, 1 = no updates
"upgradeSettings": { },
"params": ["-Syu"], "upgradeSettings": {
"requiresSudo": false "params": ["-Syu"],
}, "requiresSudo": requiresSudo
"parserSettings": { },
"lineRegex": /^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/, "parserSettings": {
"entryProducer": function (match) { "lineRegex": /^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/,
return { "entryProducer": function (match) {
"name": match[1], return {
"currentVersion": match[2], "name": match[1],
"newVersion": match[3], "currentVersion": match[2],
"description": `${match[1]} ${match[2]} ${match[3]}` "newVersion": match[3],
}; "description": `${match[1]} ${match[2]} ${match[3]}`
};
}
} }
} }
} }
@@ -92,8 +94,9 @@ Singleton {
"checkupdates": archBasedUCSettings "checkupdates": archBasedUCSettings
} }
readonly property var packageManagerParams: { readonly property var packageManagerParams: {
"yay": archBasedPMSettings, "yay": archBasedPMSettings(false),
"paru": archBasedPMSettings, "paru": archBasedPMSettings(false),
"pacman": archBasedPMSettings(true),
"dnf": fedoraBasedPMSettings "dnf": fedoraBasedPMSettings
} }
readonly property list<string> supportedDistributions: ["arch", "artix", "cachyos", "manjaro", "endeavouros", "fedora"] readonly property list<string> supportedDistributions: ["arch", "artix", "cachyos", "manjaro", "endeavouros", "fedora"]
@@ -182,7 +185,7 @@ Singleton {
Process { Process {
id: pkgManagerDetection id: pkgManagerDetection
command: ["sh", "-c", "which paru || which yay || which dnf"] command: ["sh", "-c", "which paru || which yay || which pacman || which dnf"]
onExited: exitCode => { onExited: exitCode => {
if (exitCode === 0) { if (exitCode === 0) {
@@ -259,7 +262,7 @@ Singleton {
const terminal = Quickshell.env("TERMINAL") || "xterm"; const terminal = Quickshell.env("TERMINAL") || "xterm";
if (SettingsData.updaterUseCustomCommand && SettingsData.updaterCustomCommand.length > 0) { if (SettingsData.updaterUseCustomCommand && SettingsData.updaterCustomCommand.length > 0) {
const updateCommand = `${SettingsData.updaterCustomCommand} && echo "Updates complete! Press Enter to close..." && read`; const updateCommand = `${SettingsData.updaterCustomCommand} && echo -n "Updates complete! " ; echo "Press Enter to close..." && read`;
const termClass = SettingsData.updaterTerminalAdditionalParams; const termClass = SettingsData.updaterTerminalAdditionalParams;
var finalCommand = [terminal]; var finalCommand = [terminal];
@@ -274,7 +277,7 @@ Singleton {
} else { } else {
const params = packageManagerParams[pkgManager].upgradeSettings.params.join(" "); const params = packageManagerParams[pkgManager].upgradeSettings.params.join(" ");
const sudo = packageManagerParams[pkgManager].upgradeSettings.requiresSudo ? "sudo" : ""; const sudo = packageManagerParams[pkgManager].upgradeSettings.requiresSudo ? "sudo" : "";
const updateCommand = `${sudo} ${pkgManager} ${params} && echo "Updates complete! Press Enter to close..." && read`; const updateCommand = `${sudo} ${pkgManager} ${params} && echo -n "Updates complete! " ; echo "Press Enter to close..." && read`;
updater.command = [terminal, "-e", "sh", "-c", updateCommand]; updater.command = [terminal, "-e", "sh", "-c", updateCommand];
} }
@@ -264,7 +264,7 @@ Singleton {
} }
if (process) { if (process) {
process.command = ["sh", "-c", `find "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`]; process.command = ["sh", "-c", `find -L "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`];
process.targetScreenName = screenName; process.targetScreenName = screenName;
process.currentWallpaper = currentWallpaper; process.currentWallpaper = currentWallpaper;
process.goToPrevious = false; process.goToPrevious = false;
@@ -272,7 +272,7 @@ Singleton {
} }
} else { } else {
// Use global process for fallback // Use global process for fallback
cyclingProcess.command = ["sh", "-c", `find "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`]; cyclingProcess.command = ["sh", "-c", `find -L "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`];
cyclingProcess.targetScreenName = screenName || ""; cyclingProcess.targetScreenName = screenName || "";
cyclingProcess.currentWallpaper = currentWallpaper; cyclingProcess.currentWallpaper = currentWallpaper;
cyclingProcess.running = true; cyclingProcess.running = true;
@@ -296,7 +296,7 @@ Singleton {
} }
if (process) { if (process) {
process.command = ["sh", "-c", `find "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`]; process.command = ["sh", "-c", `find -L "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`];
process.targetScreenName = screenName; process.targetScreenName = screenName;
process.currentWallpaper = currentWallpaper; process.currentWallpaper = currentWallpaper;
process.goToPrevious = true; process.goToPrevious = true;
@@ -304,7 +304,7 @@ Singleton {
} }
} else { } else {
// Use global process for fallback // Use global process for fallback
prevCyclingProcess.command = ["sh", "-c", `find "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`]; prevCyclingProcess.command = ["sh", "-c", `find -L "${wallpaperDir}" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.jxl" -o -iname "*.avif" -o -iname "*.heif" -o -iname "*.exr" \\) 2>/dev/null | sort`];
prevCyclingProcess.targetScreenName = screenName || ""; prevCyclingProcess.targetScreenName = screenName || "";
prevCyclingProcess.currentWallpaper = currentWallpaper; prevCyclingProcess.currentWallpaper = currentWallpaper;
prevCyclingProcess.running = true; prevCyclingProcess.running = true;
+1 -1
View File
@@ -1 +1 @@
v1.4.3 v1.4.4
+1 -1
View File
@@ -49,7 +49,7 @@ Item {
readonly property string iconPath: { readonly property string iconPath: {
if (hasSpecialPrefix || !iconValue) if (hasSpecialPrefix || !iconValue)
return ""; return "";
return Quickshell.iconPath(iconValue, true) || DesktopService.resolveIconPath(iconValue); return Paths.resolveIconPath(iconValue);
} }
visible: iconValue !== undefined && iconValue !== "" visible: iconValue !== undefined && iconValue !== ""
+1 -1
View File
@@ -289,7 +289,7 @@ Item {
visible: false visible: false
color: "transparent" color: "transparent"
Component.onCompleted: { Component.onCompleted: {
if (typeof updatesEnabled !== "undefined") if (typeof updatesEnabled !== "undefined" && !root.overlayContent)
updatesEnabled = false; updatesEnabled = false;
} }
+1 -1
View File
@@ -1,4 +1,4 @@
[colors] [colors-dark]
foreground={{colors.on_surface.default.hex_stripped}} foreground={{colors.on_surface.default.hex_stripped}}
background={{colors.background.default.hex_stripped}} background={{colors.background.default.hex_stripped}}
selection-foreground={{colors.on_surface.default.hex_stripped}} selection-foreground={{colors.on_surface.default.hex_stripped}}
@@ -92,3 +92,21 @@ toolbar .toolbarbutton-1 {
#zen-appcontent-navbar-container { #zen-appcontent-navbar-container {
background-color: {{colors.background.default.hex}} !important; background-color: {{colors.background.default.hex}} !important;
} }
#PanelUI-menu-button .toolbarbutton-icon,
#downloads-button .toolbarbutton-icon,
#unified-extensions-button .toolbarbutton-icon {
fill: {{colors.primary.default.hex}} !important;
color: {{colors.primary.default.hex}} !important;
}
#PanelUI-menu-button .toolbarbutton-badge-stack,
#downloads-button .toolbarbutton-badge-stack,
#unified-extensions-button .toolbarbutton-badge-stack {
fill: {{colors.primary.default.hex}} !important;
color: {{colors.primary.default.hex}} !important;
}
toolbar .toolbarbutton-1 > .toolbarbutton-icon {
fill: {{colors.primary.default.hex}} !important;
}
+2 -2
View File
@@ -4452,8 +4452,8 @@
"comment": "" "comment": ""
}, },
{ {
"term": "File search requires dsearch\nInstall from github.com/morelazers/dsearch", "term": "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch",
"context": "File search requires dsearch\nInstall from github.com/morelazers/dsearch", "context": "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch",
"reference": "Modals/DankLauncherV2/ResultsList.qml:471", "reference": "Modals/DankLauncherV2/ResultsList.qml:471",
"comment": "" "comment": ""
}, },
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "Información del archivo" "File Information": "Información del archivo"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"Files": { "Files": {
"Files": "Archivos" "Files": "Archivos"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "اطلاعات فایل" "File Information": "اطلاعات فایل"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "جستجوی فایل به dsearch نیاز دارد\nاز github.com/morelazers/dsearch نصب کنید" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": "جستجوی فایل به dsearch نیاز دارد\nاز github.com/AvengeMedia/danksearch نصب کنید"
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "جستجوی نیاز به dsearch دارد\\nاز github.com/morelazers/dsearch نصب کنید" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": "جستجوی نیاز به dsearch دارد\\nاز github.com/AvengeMedia/danksearch نصب کنید"
}, },
"Files": { "Files": {
"Files": "فایل‌ها" "Files": "فایل‌ها"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "Informations sur le fichier" "File Information": "Informations sur le fichier"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "La recherche de fichiers nécessite dsearch\nInstallez-le depuis github.com/morelazers/dsearch" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": "La recherche de fichiers nécessite dsearch\nInstallez-le depuis github.com/AvengeMedia/danksearch"
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "La recherche de fichiers nécessite dsearch\\nInstallez-le depuis github.com/morelazers/dsearch" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": "La recherche de fichiers nécessite dsearch\\nInstallez-le depuis github.com/AvengeMedia/danksearch"
}, },
"Files": { "Files": {
"Files": "Fichiers" "Files": "Fichiers"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "פרטי קובץ" "File Information": "פרטי קובץ"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "חיפוש קבצים הכלי dsearch נדרש כדי לבצע חיפוש של קבצים.\nהתקן/י אותו מ: github.com/morelazers/dsearch" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": "חיפוש קבצים הכלי dsearch נדרש כדי לבצע חיפוש של קבצים.\nהתקן/י אותו מ: github.com/AvengeMedia/danksearch"
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "חיפוש קבצים הכלי dsearch נדרש כדי לבצע חיפוש של קבצים.\\nהתקן/י אותו מ: github.com/morelazers/dsearch" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": "חיפוש קבצים הכלי dsearch נדרש כדי לבצע חיפוש של קבצים.\\nהתקן/י אותו מ: github.com/AvengeMedia/danksearch"
}, },
"Files": { "Files": {
"Files": "קבצים" "Files": "קבצים"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "Fájlinformáció" "File Information": "Fájlinformáció"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "A fájlkereséshez dsearch szükséges\nTelepítsd innen: github.com/morelazers/dsearch" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": "A fájlkereséshez dsearch szükséges\nTelepítsd innen: github.com/AvengeMedia/danksearch"
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "A fájlkereséshez dsearch szükséges\\nTelepítsd innen: github.com/morelazers/dsearch" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": "A fájlkereséshez dsearch szükséges\\nTelepítsd innen: github.com/AvengeMedia/danksearch"
}, },
"Files": { "Files": {
"Files": "Fájlok" "Files": "Fájlok"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "Informazioni File" "File Information": "Informazioni File"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "La ricerca file richiede dsearch\\nInstalla da github.com/morelazers/dsearch" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": "La ricerca file richiede dsearch\\nInstalla da github.com/AvengeMedia/danksearch"
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "La ricerca file richiede dsearch\\nInstalla da github.com/morelazers/dsearch" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": "La ricerca file richiede dsearch\\nInstalla da github.com/AvengeMedia/danksearch"
}, },
"Files": { "Files": {
"Files": "File" "Files": "File"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "ファイル情報" "File Information": "ファイル情報"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"Files": { "Files": {
"Files": "" "Files": ""
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "Bestandsinformatie" "File Information": "Bestandsinformatie"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "Bestandszoeken vereist dsearch\nInstalleer via github.com/morelazers/dsearch" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": "Bestandszoeken vereist dsearch\nInstalleer via github.com/AvengeMedia/danksearch"
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "Bestandszoeken vereist dsearch\\nInstalleer via github.com/morelazers/dsearch" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": "Bestandszoeken vereist dsearch\\nInstalleer via github.com/AvengeMedia/danksearch"
}, },
"Files": { "Files": {
"Files": "Bestanden" "Files": "Bestanden"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "Informacje" "File Information": "Informacje"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"Files": { "Files": {
"Files": "Akta" "Files": "Akta"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "Informação do Arquivo" "File Information": "Informação do Arquivo"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "Pesquisa de arquivo requer dsearch\nInstalar de github.com/morelazers/dsearch" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": "Pesquisa de arquivo requer dsearch\nInstalar de github.com/AvengeMedia/danksearch"
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "A pesquisa de arquivo requer dsearch\\nInstale em github.com/morelazers/dsearch" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": "A pesquisa de arquivo requer dsearch\\nInstale em github.com/AvengeMedia/danksearch"
}, },
"Files": { "Files": {
"Files": "Arquivos" "Files": "Arquivos"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "Dosya Bilgisi" "File Information": "Dosya Bilgisi"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"Files": { "Files": {
"Files": "Dosyalar" "Files": "Dosyalar"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "文件信息" "File Information": "文件信息"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "文件搜索需要dsearch\\n请从github.com/morelazers/dsearch进行安装" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": "文件搜索需要dsearch\\n请从github.com/AvengeMedia/danksearch进行安装"
}, },
"Files": { "Files": {
"Files": "文件" "Files": "文件"
+4 -4
View File
@@ -2023,11 +2023,11 @@
"File Information": { "File Information": {
"File Information": "檔案資訊" "File Information": "檔案資訊"
}, },
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\nInstall from github.com/morelazers/dsearch": "" "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch": ""
}, },
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": { "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": {
"File search requires dsearch\\nInstall from github.com/morelazers/dsearch": "檔案搜尋需要 dsearch\\n請從 github.com/morelazers/dsearch 安裝" "File search requires dsearch\\nInstall from github.com/AvengeMedia/danksearch": "檔案搜尋需要 dsearch\\n請從 github.com/AvengeMedia/danksearch 安裝"
}, },
"Files": { "Files": {
"Files": "檔案" "Files": "檔案"
+1 -1
View File
@@ -5194,7 +5194,7 @@
"comment": "" "comment": ""
}, },
{ {
"term": "File search requires dsearch\nInstall from github.com/morelazers/dsearch", "term": "File search requires dsearch\nInstall from github.com/AvengeMedia/danksearch",
"translation": "", "translation": "",
"context": "", "context": "",
"reference": "", "reference": "",