mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-13 07:42:46 -04:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 02a274ebe2 | |||
| fc7b61c20b | |||
| 5880043f56 | |||
| fee3b7f2a7 | |||
| c0b0339fca | |||
| 26c1e62204 | |||
| 7b2d4dbe30 | |||
| 78c5d46c6b | |||
| 3fb85df504 | |||
| 227dd24726 | |||
| ae6a656899 | |||
| a4055e0f01 | |||
| 6d98c229ef | |||
| 71d93ad85e | |||
| 4ec21fcd3d | |||
| 0a2fe03fee | |||
| 4f4745609b | |||
| a69cd515fb | |||
| 06c4b97a6b | |||
| a6cf71a190 | |||
| 21750156dc | |||
| f9b737f543 | |||
| 246b59f3b9 | |||
| dcda81ea64 | |||
| 9909b665cd | |||
| 4bcd786be3 | |||
| 64c9222000 | |||
| 12acf2dd51 | |||
| fea97b4aad | |||
| c6d398eeac | |||
| 7a74be83d7 | |||
| 67a6427418 | |||
| 18b20d3225 | |||
| 8a76885fb6 | |||
| 69b1e61ab7 |
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ Variants {
|
|||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: renderSettleTimer
|
id: renderSettleTimer
|
||||||
interval: 100
|
interval: 1000
|
||||||
onTriggered: root._renderSettling = false
|
onTriggered: root._renderSettling = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
@@ -1 +1 @@
|
|||||||
v1.4.3
|
v1.4.4
|
||||||
|
|||||||
@@ -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 !== ""
|
||||||
|
|||||||
@@ -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,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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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": ""
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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": "فایلها"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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": "קבצים"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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": ""
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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": "文件"
|
||||||
|
|||||||
@@ -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": "檔案"
|
||||||
|
|||||||
@@ -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": "",
|
||||||
|
|||||||
Reference in New Issue
Block a user