mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-31 08:52:49 -05:00
Compare commits
8 Commits
3e1c6534bd
...
7d761c4c9a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d761c4c9a | ||
|
|
4cb90c5367 | ||
|
|
1c7d15db0b | ||
|
|
7268a3fe7f | ||
|
|
d2c4391514 | ||
|
|
69b1d0c2da | ||
|
|
ba28767492 | ||
|
|
6cff5f1146 |
9
.github/workflows/go-ci.yml
vendored
9
.github/workflows/go-ci.yml
vendored
@@ -28,6 +28,15 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install flatpak
|
||||||
|
run: sudo apt update && sudo apt install -y flatpak
|
||||||
|
|
||||||
|
- name: Add flathub
|
||||||
|
run: sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||||
|
|
||||||
|
- name: Add a flatpak that mutagen could support
|
||||||
|
run: sudo flatpak install -y org.freedesktop.Platform/x86_64/24.08 app.zen_browser.zen
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
|
|||||||
78
core/internal/utils/flatpak.go
Normal file
78
core/internal/utils/flatpak.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FlatpakInPath() bool {
|
||||||
|
_, err := exec.LookPath("flatpak")
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlatpakExists(name string) bool {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("flatpak", "info", name)
|
||||||
|
err := cmd.Run()
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlatpakSearchBySubstring(substring string) bool {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("flatpak", "list", "--app")
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
out := stdout.String()
|
||||||
|
|
||||||
|
for line := range strings.SplitSeq(out, "\n") {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) > 1 {
|
||||||
|
id := fields[1]
|
||||||
|
idParts := strings.Split(id, ".")
|
||||||
|
// We are assuming that the last part of the ID is
|
||||||
|
// the package name we're looking for. This might
|
||||||
|
// not always be true, some developers use arbitrary
|
||||||
|
// suffixes.
|
||||||
|
if len(idParts) > 0 && idParts[len(idParts)-1] == substring {
|
||||||
|
cmd := exec.Command("flatpak", "info", id)
|
||||||
|
err := cmd.Run()
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlatpakInstallationDir(name string) (string, error) {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
return "", errors.New("flatpak not found in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("flatpak", "info", "--show-location", name)
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return "", errors.New("flatpak not installed: " + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
location := strings.TrimSpace(stdout.String())
|
||||||
|
if location == "" {
|
||||||
|
return "", errors.New("installation directory not found for: " + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return location, nil
|
||||||
|
}
|
||||||
210
core/internal/utils/flatpak_test.go
Normal file
210
core/internal/utils/flatpak_test.go
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFlatpakInPathAvailable(t *testing.T) {
|
||||||
|
result := FlatpakInPath()
|
||||||
|
if !result {
|
||||||
|
t.Skip("flatpak not in PATH")
|
||||||
|
}
|
||||||
|
if !result {
|
||||||
|
t.Errorf("expected true when flatpak is in PATH")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakInPathUnavailable(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
t.Setenv("PATH", tempDir)
|
||||||
|
|
||||||
|
result := FlatpakInPath()
|
||||||
|
if result {
|
||||||
|
t.Errorf("expected false when flatpak not in PATH, got true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakExistsValidPackage(t *testing.T) {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
t.Skip("flatpak not in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := FlatpakExists("com.nonexistent.package.test")
|
||||||
|
if result {
|
||||||
|
t.Logf("package exists (unexpected but not an error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakExistsNoFlatpak(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
t.Setenv("PATH", tempDir)
|
||||||
|
|
||||||
|
result := FlatpakExists("any.package.name")
|
||||||
|
if result {
|
||||||
|
t.Errorf("expected false when flatpak not in PATH, got true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakSearchBySubstringNoFlatpak(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
t.Setenv("PATH", tempDir)
|
||||||
|
|
||||||
|
result := FlatpakSearchBySubstring("test")
|
||||||
|
if result {
|
||||||
|
t.Errorf("expected false when flatpak not in PATH, got true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakSearchBySubstringNonexistent(t *testing.T) {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
t.Skip("flatpak not in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := FlatpakSearchBySubstring("ThisIsAVeryUnlikelyPackageName12345")
|
||||||
|
if result {
|
||||||
|
t.Errorf("expected false for nonexistent package substring")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakInstallationDirNoFlatpak(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
t.Setenv("PATH", tempDir)
|
||||||
|
|
||||||
|
_, err := FlatpakInstallationDir("any.package.name")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error when flatpak not in PATH")
|
||||||
|
}
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "not found in PATH") {
|
||||||
|
t.Errorf("expected 'not found in PATH' error, got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakInstallationDirNonexistent(t *testing.T) {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
t.Skip("flatpak not in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := FlatpakInstallationDir("com.nonexistent.package.test")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error for nonexistent package")
|
||||||
|
}
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "not installed") {
|
||||||
|
t.Errorf("expected 'not installed' error, got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakInstallationDirValid(t *testing.T) {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
t.Skip("flatpak not in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test requires a known installed flatpak
|
||||||
|
// We can't guarantee any specific flatpak is installed,
|
||||||
|
// so we'll skip if we can't find a common one
|
||||||
|
commonFlatpaks := []string{
|
||||||
|
"org.mozilla.firefox",
|
||||||
|
"org.gnome.Calculator",
|
||||||
|
"org.freedesktop.Platform",
|
||||||
|
}
|
||||||
|
|
||||||
|
var testPackage string
|
||||||
|
for _, pkg := range commonFlatpaks {
|
||||||
|
if FlatpakExists(pkg) {
|
||||||
|
testPackage = pkg
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if testPackage == "" {
|
||||||
|
t.Skip("no common flatpak packages found for testing")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := FlatpakInstallationDir(testPackage)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if result == "" {
|
||||||
|
t.Errorf("expected non-empty installation directory")
|
||||||
|
}
|
||||||
|
if !strings.Contains(result, testPackage) {
|
||||||
|
t.Logf("installation directory %s doesn't contain package name (may be expected)", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakExistsCommandFailure(t *testing.T) {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
t.Skip("flatpak not in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock a failing flatpak command through PATH interception
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
fakeFlatpak := filepath.Join(tempDir, "flatpak")
|
||||||
|
|
||||||
|
script := "#!/bin/sh\nexit 1\n"
|
||||||
|
err := os.WriteFile(fakeFlatpak, []byte(script), 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create fake flatpak: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
originalPath := os.Getenv("PATH")
|
||||||
|
t.Setenv("PATH", tempDir+":"+originalPath)
|
||||||
|
|
||||||
|
result := FlatpakExists("test.package")
|
||||||
|
if result {
|
||||||
|
t.Errorf("expected false when flatpak command fails, got true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakSearchBySubstringCommandFailure(t *testing.T) {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
t.Skip("flatpak not in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock a failing flatpak command through PATH interception
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
fakeFlatpak := filepath.Join(tempDir, "flatpak")
|
||||||
|
|
||||||
|
script := "#!/bin/sh\nexit 1\n"
|
||||||
|
err := os.WriteFile(fakeFlatpak, []byte(script), 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create fake flatpak: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
originalPath := os.Getenv("PATH")
|
||||||
|
t.Setenv("PATH", tempDir+":"+originalPath)
|
||||||
|
|
||||||
|
result := FlatpakSearchBySubstring("test")
|
||||||
|
if result {
|
||||||
|
t.Errorf("expected false when flatpak command fails, got true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatpakInstallationDirCommandFailure(t *testing.T) {
|
||||||
|
if !FlatpakInPath() {
|
||||||
|
t.Skip("flatpak not in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock a failing flatpak command through PATH interception
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
fakeFlatpak := filepath.Join(tempDir, "flatpak")
|
||||||
|
|
||||||
|
script := "#!/bin/sh\nexit 1\n"
|
||||||
|
err := os.WriteFile(fakeFlatpak, []byte(script), 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create fake flatpak: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
originalPath := os.Getenv("PATH")
|
||||||
|
t.Setenv("PATH", tempDir+":"+originalPath)
|
||||||
|
|
||||||
|
_, err = FlatpakInstallationDir("test.package")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error when flatpak command fails")
|
||||||
|
}
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "not installed") {
|
||||||
|
t.Errorf("expected 'not installed' error, got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,10 +15,93 @@ in
|
|||||||
niri = {
|
niri = {
|
||||||
enableKeybinds = lib.mkEnableOption "DankMaterialShell niri keybinds";
|
enableKeybinds = lib.mkEnableOption "DankMaterialShell niri keybinds";
|
||||||
enableSpawn = lib.mkEnableOption "DankMaterialShell niri spawn-at-startup";
|
enableSpawn = lib.mkEnableOption "DankMaterialShell niri spawn-at-startup";
|
||||||
|
includes = {
|
||||||
|
enable = (lib.mkEnableOption "includes for niri-flake") // {
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
override = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
description = ''
|
||||||
|
Whether DMS settings will be prioritized over settings defined in niri-flake or not
|
||||||
|
'';
|
||||||
|
default = true;
|
||||||
|
example = false;
|
||||||
|
};
|
||||||
|
originalFileName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = ''
|
||||||
|
A new name for the config file generated by niri-flake
|
||||||
|
'';
|
||||||
|
default = "hm";
|
||||||
|
example = "niri-flake";
|
||||||
|
};
|
||||||
|
filesToInclude = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
description = ''
|
||||||
|
A list of dms-generated files to include
|
||||||
|
'';
|
||||||
|
default = [
|
||||||
|
"alttab"
|
||||||
|
"binds"
|
||||||
|
"colors"
|
||||||
|
"layout"
|
||||||
|
"outputs"
|
||||||
|
"wpblur"
|
||||||
|
];
|
||||||
|
example = [
|
||||||
|
"outputs"
|
||||||
|
"wpblur"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
warnings = (
|
||||||
|
lib.optional (cfg.niri.enableKeybinds && cfg.niri.includes.enable) ''
|
||||||
|
It is not recommended to use both `enableKeybinds` and `includes.enable` at the same time.
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|
||||||
|
# HACK: niri-flake does not support config includes yet, but we can "fix" that
|
||||||
|
# TODO: replace with proper config includes after https://github.com/sodiboo/niri-flake/pull/1548 merge
|
||||||
|
xdg.configFile = lib.mkIf cfg.niri.includes.enable (
|
||||||
|
let
|
||||||
|
cfg' = cfg.niri.includes;
|
||||||
|
|
||||||
|
withOriginalConfig =
|
||||||
|
dmsFiles:
|
||||||
|
if cfg'.override then
|
||||||
|
[ cfg'.originalFileName ] ++ dmsFiles
|
||||||
|
else
|
||||||
|
dmsFiles ++ [ cfg'.originalFileName ];
|
||||||
|
|
||||||
|
fixes = map (fix: "\n${fix}") (
|
||||||
|
lib.optional (cfg'.enable && config.programs.niri.settings.layout.border.enable)
|
||||||
|
# kdl
|
||||||
|
''
|
||||||
|
// Border fix
|
||||||
|
// See https://yalter.github.io/niri/Configuration%3A-Include.html#border-special-case for details
|
||||||
|
layout { border { on; }; }
|
||||||
|
''
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
niri-config.target = lib.mkForce "niri/${cfg'.originalFileName}.kdl";
|
||||||
|
niri-config-dms = {
|
||||||
|
target = "niri/config.kdl";
|
||||||
|
text = lib.pipe cfg'.filesToInclude [
|
||||||
|
(map (filename: "dms/${filename}"))
|
||||||
|
withOriginalConfig
|
||||||
|
(map (filename: "include \"${filename}.kdl\""))
|
||||||
|
(files: files ++ fixes)
|
||||||
|
(builtins.concatStringsSep "\n")
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
programs.niri.settings = lib.mkMerge [
|
programs.niri.settings = lib.mkMerge [
|
||||||
(lib.mkIf cfg.niri.enableKeybinds {
|
(lib.mkIf cfg.niri.enableKeybinds {
|
||||||
binds =
|
binds =
|
||||||
|
|||||||
@@ -171,9 +171,11 @@ Singleton {
|
|||||||
]
|
]
|
||||||
|
|
||||||
property bool showWorkspaceIndex: false
|
property bool showWorkspaceIndex: false
|
||||||
|
property bool showWorkspaceName: false
|
||||||
property bool showWorkspacePadding: false
|
property bool showWorkspacePadding: false
|
||||||
property bool workspaceScrolling: false
|
property bool workspaceScrolling: false
|
||||||
property bool showWorkspaceApps: false
|
property bool showWorkspaceApps: false
|
||||||
|
property bool groupWorkspaceApps: true
|
||||||
property int maxWorkspaceIcons: 3
|
property int maxWorkspaceIcons: 3
|
||||||
property bool workspacesPerMonitor: true
|
property bool workspacesPerMonitor: true
|
||||||
property bool showOccupiedWorkspacesOnly: false
|
property bool showOccupiedWorkspacesOnly: false
|
||||||
@@ -183,7 +185,7 @@ Singleton {
|
|||||||
property bool waveProgressEnabled: true
|
property bool waveProgressEnabled: true
|
||||||
property bool scrollTitleEnabled: true
|
property bool scrollTitleEnabled: true
|
||||||
property bool audioVisualizerEnabled: true
|
property bool audioVisualizerEnabled: true
|
||||||
property bool audioScrollEnabled: true
|
property string audioScrollMode: "volume"
|
||||||
property bool clockCompactMode: false
|
property bool clockCompactMode: false
|
||||||
property bool focusedWindowCompactMode: false
|
property bool focusedWindowCompactMode: false
|
||||||
property bool runningAppsCompactMode: true
|
property bool runningAppsCompactMode: true
|
||||||
@@ -798,9 +800,9 @@ Singleton {
|
|||||||
|
|
||||||
Store.parse(root, obj);
|
Store.parse(root, obj);
|
||||||
|
|
||||||
if (obj.weatherLocation !== undefined)
|
if (obj?.weatherLocation !== undefined)
|
||||||
_legacyWeatherLocation = obj.weatherLocation;
|
_legacyWeatherLocation = obj.weatherLocation;
|
||||||
if (obj.weatherCoordinates !== undefined)
|
if (obj?.weatherCoordinates !== undefined)
|
||||||
_legacyWeatherCoordinates = obj.weatherCoordinates;
|
_legacyWeatherCoordinates = obj.weatherCoordinates;
|
||||||
|
|
||||||
_loadedSettingsSnapshot = JSON.stringify(Store.toJson(root));
|
_loadedSettingsSnapshot = JSON.stringify(Store.toJson(root));
|
||||||
|
|||||||
@@ -4,12 +4,15 @@
|
|||||||
|
|
||||||
function parse(root, jsonObj) {
|
function parse(root, jsonObj) {
|
||||||
var SPEC = SpecModule.SPEC;
|
var SPEC = SpecModule.SPEC;
|
||||||
for (var k in SPEC) {
|
|
||||||
root[k] = SPEC[k].def;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!jsonObj) return;
|
if (!jsonObj) return;
|
||||||
|
|
||||||
|
for (var k in SPEC) {
|
||||||
|
if (!(k in jsonObj)) {
|
||||||
|
root[k] = SPEC[k].def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var k in jsonObj) {
|
for (var k in jsonObj) {
|
||||||
if (!SPEC[k]) continue;
|
if (!SPEC[k]) continue;
|
||||||
var raw = jsonObj[k];
|
var raw = jsonObj[k];
|
||||||
|
|||||||
@@ -81,10 +81,12 @@ var SPEC = {
|
|||||||
]},
|
]},
|
||||||
|
|
||||||
showWorkspaceIndex: { def: false },
|
showWorkspaceIndex: { def: false },
|
||||||
|
showWorkspaceName: { def: false },
|
||||||
showWorkspacePadding: { def: false },
|
showWorkspacePadding: { def: false },
|
||||||
workspaceScrolling: { def: false },
|
workspaceScrolling: { def: false },
|
||||||
showWorkspaceApps: { def: false },
|
showWorkspaceApps: { def: false },
|
||||||
maxWorkspaceIcons: { def: 3 },
|
maxWorkspaceIcons: { def: 3 },
|
||||||
|
groupWorkspaceApps: { def: true },
|
||||||
workspacesPerMonitor: { def: true },
|
workspacesPerMonitor: { def: true },
|
||||||
showOccupiedWorkspacesOnly: { def: false },
|
showOccupiedWorkspacesOnly: { def: false },
|
||||||
reverseScrolling: { def: false },
|
reverseScrolling: { def: false },
|
||||||
@@ -93,7 +95,7 @@ var SPEC = {
|
|||||||
waveProgressEnabled: { def: true },
|
waveProgressEnabled: { def: true },
|
||||||
scrollTitleEnabled: { def: true },
|
scrollTitleEnabled: { def: true },
|
||||||
audioVisualizerEnabled: { def: true },
|
audioVisualizerEnabled: { def: true },
|
||||||
audioScrollEnabled: { def: true },
|
audioScrollMode: { def: "volume" },
|
||||||
clockCompactMode: { def: false },
|
clockCompactMode: { def: false },
|
||||||
focusedWindowCompactMode: { def: false },
|
focusedWindowCompactMode: { def: false },
|
||||||
runningAppsCompactMode: { def: true },
|
runningAppsCompactMode: { def: true },
|
||||||
|
|||||||
@@ -4,14 +4,16 @@
|
|||||||
|
|
||||||
function parse(root, jsonObj) {
|
function parse(root, jsonObj) {
|
||||||
var SPEC = SpecModule.SPEC;
|
var SPEC = SpecModule.SPEC;
|
||||||
for (var k in SPEC) {
|
|
||||||
if (k === "pluginSettings") continue;
|
|
||||||
var spec = SPEC[k];
|
|
||||||
root[k] = spec.def;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!jsonObj) return;
|
if (!jsonObj) return;
|
||||||
|
|
||||||
|
for (var k in SPEC) {
|
||||||
|
if (k === "pluginSettings") continue;
|
||||||
|
if (!(k in jsonObj)) {
|
||||||
|
root[k] = SPEC[k].def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var k in jsonObj) {
|
for (var k in jsonObj) {
|
||||||
if (!SPEC[k]) continue;
|
if (!SPEC[k]) continue;
|
||||||
if (k === "pluginSettings") continue;
|
if (k === "pluginSettings") continue;
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ Item {
|
|||||||
function onPluginLoaded() { updateCategories() }
|
function onPluginLoaded() { updateCategories() }
|
||||||
function onPluginUnloaded() { updateCategories() }
|
function onPluginUnloaded() { updateCategories() }
|
||||||
function onPluginListUpdated() { updateCategories() }
|
function onPluginListUpdated() { updateCategories() }
|
||||||
|
function onRequestLauncherUpdate(pluginId) {
|
||||||
|
// Only update if we are actually looking at this plugin or in All category
|
||||||
|
updateFilteredModel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ BasePill {
|
|||||||
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : clockRow.implicitWidth
|
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : clockRow.implicitWidth
|
||||||
implicitHeight: root.isVerticalOrientation ? clockColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
implicitHeight: root.isVerticalOrientation ? clockColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
|
||||||
|
|
||||||
|
readonly property bool compact: widgetData?.clockCompactMode !== undefined ? widgetData.clockCompactMode : SettingsData.clockCompactMode
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: clockColumn
|
id: clockColumn
|
||||||
visible: root.isVerticalOrientation
|
visible: root.isVerticalOrientation
|
||||||
@@ -106,6 +108,7 @@ BasePill {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: Theme.spacingM
|
height: Theme.spacingM
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: !compact
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width * 0.6
|
width: parent.width * 0.6
|
||||||
@@ -118,6 +121,7 @@ BasePill {
|
|||||||
Row {
|
Row {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: !compact
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
@@ -151,6 +155,7 @@ BasePill {
|
|||||||
Row {
|
Row {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: !compact
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: {
|
text: {
|
||||||
@@ -204,7 +209,7 @@ BasePill {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.outlineButton
|
color: Theme.outlineButton
|
||||||
anchors.baseline: dateText.baseline
|
anchors.baseline: dateText.baseline
|
||||||
visible: !(widgetData?.clockCompactMode !== undefined ? widgetData.clockCompactMode : SettingsData.clockCompactMode)
|
visible: !compact
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -218,7 +223,7 @@ BasePill {
|
|||||||
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale)
|
||||||
color: Theme.widgetTextColor
|
color: Theme.widgetTextColor
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: !(widgetData?.clockCompactMode !== undefined ? widgetData.clockCompactMode : SettingsData.clockCompactMode)
|
visible: !compact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,38 +54,67 @@ BasePill {
|
|||||||
property real touchpadThreshold: 100
|
property real touchpadThreshold: 100
|
||||||
|
|
||||||
onWheel: function (wheelEvent) {
|
onWheel: function (wheelEvent) {
|
||||||
if (!usePlayerVolume)
|
if (SettingsData.audioScrollMode === "nothing")
|
||||||
return;
|
|
||||||
if (!SettingsData.audioScrollEnabled)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wheelEvent.accepted = true;
|
if (SettingsData.audioScrollMode === "volume") {
|
||||||
|
if (!usePlayerVolume)
|
||||||
|
return;
|
||||||
|
|
||||||
const deltaY = wheelEvent.angleDelta.y;
|
wheelEvent.accepted = true;
|
||||||
const isMouseWheelY = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;
|
|
||||||
|
|
||||||
const currentVolume = activePlayer.volume * 100;
|
const deltaY = wheelEvent.angleDelta.y;
|
||||||
|
const isMouseWheelY = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;
|
||||||
|
|
||||||
let newVolume = currentVolume;
|
const currentVolume = activePlayer.volume * 100;
|
||||||
if (isMouseWheelY) {
|
|
||||||
if (deltaY > 0) {
|
let newVolume = currentVolume;
|
||||||
newVolume = Math.min(100, currentVolume + 5);
|
if (isMouseWheelY) {
|
||||||
} else if (deltaY < 0) {
|
if (deltaY > 0) {
|
||||||
newVolume = Math.max(0, currentVolume - 5);
|
newVolume = Math.min(100, currentVolume + 5);
|
||||||
}
|
} else if (deltaY < 0) {
|
||||||
} else {
|
newVolume = Math.max(0, currentVolume - 5);
|
||||||
scrollAccumulatorY += deltaY;
|
}
|
||||||
if (Math.abs(scrollAccumulatorY) >= touchpadThreshold) {
|
} else {
|
||||||
if (scrollAccumulatorY > 0) {
|
scrollAccumulatorY += deltaY;
|
||||||
newVolume = Math.min(100, currentVolume + 1);
|
if (Math.abs(scrollAccumulatorY) >= touchpadThreshold) {
|
||||||
} else {
|
if (scrollAccumulatorY > 0) {
|
||||||
newVolume = Math.max(0, currentVolume - 1);
|
newVolume = Math.min(100, currentVolume + 1);
|
||||||
|
} else {
|
||||||
|
newVolume = Math.max(0, currentVolume - 1);
|
||||||
|
}
|
||||||
|
scrollAccumulatorY = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activePlayer.volume = newVolume / 100;
|
||||||
|
} else if (SettingsData.audioScrollMode === "song") {
|
||||||
|
if (!activePlayer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wheelEvent.accepted = true;
|
||||||
|
|
||||||
|
const deltaY = wheelEvent.angleDelta.y;
|
||||||
|
const isMouseWheelY = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0;
|
||||||
|
|
||||||
|
if (isMouseWheelY) {
|
||||||
|
if (deltaY > 0) {
|
||||||
|
activePlayer.previous();
|
||||||
|
} else {
|
||||||
|
activePlayer.next();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scrollAccumulatorY += deltaY;
|
||||||
|
if (Math.abs(scrollAccumulatorY) >= touchpadThreshold) {
|
||||||
|
if (scrollAccumulatorY > 0) {
|
||||||
|
activePlayer.previous();
|
||||||
|
} else {
|
||||||
|
activePlayer.next();
|
||||||
|
}
|
||||||
|
scrollAccumulatorY = 0;
|
||||||
}
|
}
|
||||||
scrollAccumulatorY = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activePlayer.volume = newVolume / 100;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const keyBase = (w.app_id || w.appId || w.class || w.windowClass || "unknown");
|
const keyBase = (w.app_id || w.appId || w.class || w.windowClass || "unknown");
|
||||||
const key = isActiveWs ? `${keyBase}_${i}` : keyBase;
|
const key = isActiveWs || !SettingsData.groupWorkspaceApps ? `${keyBase}_${i}` : keyBase;
|
||||||
|
|
||||||
if (!byApp[key]) {
|
if (!byApp[key]) {
|
||||||
const moddedId = Paths.moddedAppId(keyBase);
|
const moddedId = Paths.moddedAppId(keyBase);
|
||||||
@@ -565,6 +565,17 @@ Item {
|
|||||||
if (isPlaceholder)
|
if (isPlaceholder)
|
||||||
return index + 1;
|
return index + 1;
|
||||||
|
|
||||||
|
if (SettingsData.showWorkspaceName) {
|
||||||
|
let workspaceName = modelData?.name;
|
||||||
|
|
||||||
|
if (workspaceName && workspaceName !== "") {
|
||||||
|
if (root.isVertical) {
|
||||||
|
return workspaceName.charAt(0);
|
||||||
|
}
|
||||||
|
return workspaceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (root.useExtWorkspace)
|
if (root.useExtWorkspace)
|
||||||
return index + 1;
|
return index + 1;
|
||||||
if (CompositorService.isHyprland)
|
if (CompositorService.isHyprland)
|
||||||
@@ -942,7 +953,7 @@ Item {
|
|||||||
id: rowLayout
|
id: rowLayout
|
||||||
Row {
|
Row {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
visible: loadedIcons.length > 0 || SettingsData.showWorkspaceIndex || loadedHasIcon
|
visible: loadedIcons.length > 0 || SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName || loadedHasIcon
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: loadedHasIcon && loadedIconData?.type === "icon"
|
visible: loadedHasIcon && loadedIconData?.type === "icon"
|
||||||
@@ -975,7 +986,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: SettingsData.showWorkspaceIndex && !loadedHasIcon
|
visible: (SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon
|
||||||
width: wsIndexText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0)
|
width: wsIndexText.implicitWidth + (isActive && loadedIcons.length > 0 ? 4 : 0)
|
||||||
height: root.appIconSize
|
height: root.appIconSize
|
||||||
|
|
||||||
@@ -1072,7 +1083,7 @@ Item {
|
|||||||
id: columnLayout
|
id: columnLayout
|
||||||
Column {
|
Column {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
visible: loadedIcons.length > 0 || loadedHasIcon
|
visible: loadedIcons.length > 0 || SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName || loadedHasIcon
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
visible: loadedHasIcon && loadedIconData?.type === "icon"
|
visible: loadedHasIcon && loadedIconData?.type === "icon"
|
||||||
@@ -1209,7 +1220,7 @@ Item {
|
|||||||
Loader {
|
Loader {
|
||||||
id: indexLoader
|
id: indexLoader
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: SettingsData.showWorkspaceIndex && !loadedHasIcon && !SettingsData.showWorkspaceApps
|
active: (SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName) && !loadedHasIcon && !SettingsData.showWorkspaceApps
|
||||||
sourceComponent: Item {
|
sourceComponent: Item {
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|||||||
@@ -46,11 +46,24 @@ Item {
|
|||||||
onToggled: checked => SettingsData.set("audioVisualizerEnabled", checked)
|
onToggled: checked => SettingsData.set("audioVisualizerEnabled", checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsToggleRow {
|
SettingsDropdownRow {
|
||||||
|
property var scrollOpts: {
|
||||||
|
"Change Volume": "volume",
|
||||||
|
"Change Song": "song",
|
||||||
|
"Nothing": "nothing"
|
||||||
|
}
|
||||||
|
|
||||||
text: I18n.tr("Scroll Wheel")
|
text: I18n.tr("Scroll Wheel")
|
||||||
description: I18n.tr("Scroll on widget changes media volume")
|
description: I18n.tr("Scroll wheel behavior on media widget")
|
||||||
checked: SettingsData.audioScrollEnabled
|
settingKey: "audioScrollMode"
|
||||||
onToggled: checked => SettingsData.set("audioScrollEnabled", checked)
|
tags: ["media", "music", "scroll"]
|
||||||
|
options: Object.keys(scrollOpts).sort()
|
||||||
|
currentValue: {
|
||||||
|
Object.keys(scrollOpts).find(key => scrollOpts[key] === SettingsData.audioScrollMode) ?? "volume"
|
||||||
|
}
|
||||||
|
onValueChanged: value => {
|
||||||
|
SettingsData.set("audioScrollMode", scrollOpts[value])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,15 @@ Item {
|
|||||||
onToggled: checked => SettingsData.set("showWorkspaceIndex", checked)
|
onToggled: checked => SettingsData.set("showWorkspaceIndex", checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
settingKey: "showWorkspaceName"
|
||||||
|
tags: ["workspace", "name", "labels"]
|
||||||
|
text: I18n.tr("Workspace Names")
|
||||||
|
description: I18n.tr("Show workspace name on horizontal bars, and first letter on vertical bars")
|
||||||
|
checked: SettingsData.showWorkspaceName
|
||||||
|
onToggled: checked => SettingsData.set("showWorkspaceName", checked)
|
||||||
|
}
|
||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
settingKey: "showWorkspacePadding"
|
settingKey: "showWorkspacePadding"
|
||||||
tags: ["workspace", "padding", "minimum"]
|
tags: ["workspace", "padding", "minimum"]
|
||||||
@@ -94,6 +103,16 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
settingKey: "groupWorkspaceApps"
|
||||||
|
tags: ["workspace", "apps", "icons", "group", "grouped", "collapse"]
|
||||||
|
text: I18n.tr("Group Workspace Apps")
|
||||||
|
description: I18n.tr("Group repeated application icons in unfocused workspaces")
|
||||||
|
checked: SettingsData.groupWorkspaceApps
|
||||||
|
visible: SettingsData.showWorkspaceApps
|
||||||
|
onToggled: checked => SettingsData.set("groupWorkspaceApps", checked)
|
||||||
|
}
|
||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
settingKey: "workspacesPerMonitor"
|
settingKey: "workspacesPerMonitor"
|
||||||
tags: ["workspace", "per-monitor", "multi-monitor"]
|
tags: ["workspace", "per-monitor", "multi-monitor"]
|
||||||
|
|||||||
@@ -411,26 +411,43 @@ Singleton {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const component = PluginService.pluginLauncherComponents[pluginId]
|
let instance = PluginService.pluginInstances[pluginId]
|
||||||
if (!component)
|
let isPersistent = true
|
||||||
|
|
||||||
|
if (!instance) {
|
||||||
|
const component = PluginService.pluginLauncherComponents[pluginId]
|
||||||
|
if (!component)
|
||||||
|
return []
|
||||||
|
|
||||||
|
try {
|
||||||
|
instance = component.createObject(root, {
|
||||||
|
"pluginService": PluginService
|
||||||
|
})
|
||||||
|
isPersistent = false
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("AppSearchService: Error creating temporary plugin instance", pluginId, ":", e)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!instance)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const instance = component.createObject(root, {
|
if (typeof instance.getItems === "function") {
|
||||||
"pluginService": PluginService
|
|
||||||
})
|
|
||||||
|
|
||||||
if (instance && typeof instance.getItems === "function") {
|
|
||||||
const items = instance.getItems(query || "")
|
const items = instance.getItems(query || "")
|
||||||
instance.destroy()
|
if (!isPersistent)
|
||||||
|
instance.destroy()
|
||||||
return items || []
|
return items || []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance) {
|
if (!isPersistent) {
|
||||||
instance.destroy()
|
instance.destroy()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("AppSearchService: Error getting items from plugin", pluginId, ":", e)
|
console.warn("AppSearchService: Error getting items from plugin", pluginId, ":", e)
|
||||||
|
if (!isPersistent)
|
||||||
|
instance.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
return []
|
return []
|
||||||
@@ -440,26 +457,43 @@ Singleton {
|
|||||||
if (typeof PluginService === "undefined")
|
if (typeof PluginService === "undefined")
|
||||||
return false
|
return false
|
||||||
|
|
||||||
const component = PluginService.pluginLauncherComponents[pluginId]
|
let instance = PluginService.pluginInstances[pluginId]
|
||||||
if (!component)
|
let isPersistent = true
|
||||||
|
|
||||||
|
if (!instance) {
|
||||||
|
const component = PluginService.pluginLauncherComponents[pluginId]
|
||||||
|
if (!component)
|
||||||
|
return false
|
||||||
|
|
||||||
|
try {
|
||||||
|
instance = component.createObject(root, {
|
||||||
|
"pluginService": PluginService
|
||||||
|
})
|
||||||
|
isPersistent = false
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("AppSearchService: Error creating temporary plugin instance for execution", pluginId, ":", e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!instance)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const instance = component.createObject(root, {
|
if (typeof instance.executeItem === "function") {
|
||||||
"pluginService": PluginService
|
|
||||||
})
|
|
||||||
|
|
||||||
if (instance && typeof instance.executeItem === "function") {
|
|
||||||
instance.executeItem(item)
|
instance.executeItem(item)
|
||||||
instance.destroy()
|
if (!isPersistent)
|
||||||
|
instance.destroy()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance) {
|
if (!isPersistent) {
|
||||||
instance.destroy()
|
instance.destroy()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("AppSearchService: Error executing item from plugin", pluginId, ":", e)
|
console.warn("AppSearchService: Error executing item from plugin", pluginId, ":", e)
|
||||||
|
if (!isPersistent)
|
||||||
|
instance.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ Singleton {
|
|||||||
signal pluginDataChanged(string pluginId)
|
signal pluginDataChanged(string pluginId)
|
||||||
signal pluginListUpdated
|
signal pluginListUpdated
|
||||||
signal globalVarChanged(string pluginId, string varName)
|
signal globalVarChanged(string pluginId, string varName)
|
||||||
|
signal requestLauncherUpdate(string pluginId)
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: resyncDebounce
|
id: resyncDebounce
|
||||||
@@ -286,12 +287,14 @@ Singleton {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDaemon) {
|
// MODIFICATION: Treat Launchers as persistent instances like Daemons
|
||||||
|
if (isDaemon || isLauncher) {
|
||||||
const instance = comp.createObject(root, {
|
const instance = comp.createObject(root, {
|
||||||
"pluginId": pluginId
|
"pluginId": pluginId,
|
||||||
|
"pluginService": root // Inject PluginService
|
||||||
});
|
});
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
console.error("PluginService: failed to instantiate daemon:", pluginId, comp.errorString());
|
console.error("PluginService: failed to instantiate plugin:", pluginId, comp.errorString());
|
||||||
pluginLoadFailed(pluginId, comp.errorString());
|
pluginLoadFailed(pluginId, comp.errorString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -299,13 +302,15 @@ Singleton {
|
|||||||
newInstances[pluginId] = instance;
|
newInstances[pluginId] = instance;
|
||||||
pluginInstances = newInstances;
|
pluginInstances = newInstances;
|
||||||
|
|
||||||
const newDaemons = Object.assign({}, pluginDaemonComponents);
|
if (isDaemon) {
|
||||||
newDaemons[pluginId] = comp;
|
const newDaemons = Object.assign({}, pluginDaemonComponents);
|
||||||
pluginDaemonComponents = newDaemons;
|
newDaemons[pluginId] = comp;
|
||||||
} else if (isLauncher) {
|
pluginDaemonComponents = newDaemons;
|
||||||
const newLaunchers = Object.assign({}, pluginLauncherComponents);
|
} else {
|
||||||
newLaunchers[pluginId] = comp;
|
const newLaunchers = Object.assign({}, pluginLauncherComponents);
|
||||||
pluginLauncherComponents = newLaunchers;
|
newLaunchers[pluginId] = comp;
|
||||||
|
pluginLauncherComponents = newLaunchers;
|
||||||
|
}
|
||||||
} else if (isDesktop) {
|
} else if (isDesktop) {
|
||||||
const newDesktop = Object.assign({}, pluginDesktopComponents);
|
const newDesktop = Object.assign({}, pluginDesktopComponents);
|
||||||
newDesktop[pluginId] = comp;
|
newDesktop[pluginId] = comp;
|
||||||
|
|||||||
@@ -708,6 +708,31 @@
|
|||||||
],
|
],
|
||||||
"icon": "visibility_off"
|
"icon": "visibility_off"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "groupWorkspaceApps",
|
||||||
|
"label": "Group Workspace Apps",
|
||||||
|
"tabIndex": 4,
|
||||||
|
"category": "Workspaces",
|
||||||
|
"keywords": [
|
||||||
|
"app",
|
||||||
|
"application",
|
||||||
|
"apps",
|
||||||
|
"collapse",
|
||||||
|
"desktop",
|
||||||
|
"group",
|
||||||
|
"grouped",
|
||||||
|
"icons",
|
||||||
|
"program",
|
||||||
|
"repeated",
|
||||||
|
"same",
|
||||||
|
"spaces",
|
||||||
|
"virtual",
|
||||||
|
"virtual desktops",
|
||||||
|
"workspace",
|
||||||
|
"workspaces"
|
||||||
|
],
|
||||||
|
"description": "Group repeated application icons in the same workspace"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "workspaceIcons",
|
"section": "workspaceIcons",
|
||||||
"label": "Named Workspace Icons",
|
"label": "Named Workspace Icons",
|
||||||
@@ -871,6 +896,34 @@
|
|||||||
],
|
],
|
||||||
"description": "Show workspace index numbers in the top bar workspace switcher"
|
"description": "Show workspace index numbers in the top bar workspace switcher"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "showWorkspaceName",
|
||||||
|
"label": "Workspace Names",
|
||||||
|
"tabIndex": 4,
|
||||||
|
"category": "Workspaces",
|
||||||
|
"keywords": [
|
||||||
|
"bars",
|
||||||
|
"desktop",
|
||||||
|
"first",
|
||||||
|
"horizontal",
|
||||||
|
"labels",
|
||||||
|
"letter",
|
||||||
|
"name",
|
||||||
|
"names",
|
||||||
|
"panel",
|
||||||
|
"show",
|
||||||
|
"spaces",
|
||||||
|
"statusbar",
|
||||||
|
"taskbar",
|
||||||
|
"topbar",
|
||||||
|
"vertical",
|
||||||
|
"virtual",
|
||||||
|
"virtual desktops",
|
||||||
|
"workspace",
|
||||||
|
"workspaces"
|
||||||
|
],
|
||||||
|
"description": "Show workspace name on horizontal bars, and first letter on vertical bars"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "showWorkspacePadding",
|
"section": "showWorkspacePadding",
|
||||||
"label": "Workspace Padding",
|
"label": "Workspace Padding",
|
||||||
@@ -2496,6 +2549,27 @@
|
|||||||
"icon": "lock",
|
"icon": "lock",
|
||||||
"description": "If the field is hidden, it will appear as soon as a key is pressed."
|
"description": "If the field is hidden, it will appear as soon as a key is pressed."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "lockBeforeSuspend",
|
||||||
|
"label": "Lock before suspend",
|
||||||
|
"tabIndex": 11,
|
||||||
|
"category": "Lock Screen",
|
||||||
|
"keywords": [
|
||||||
|
"automatic",
|
||||||
|
"automatically",
|
||||||
|
"before",
|
||||||
|
"lock",
|
||||||
|
"login",
|
||||||
|
"password",
|
||||||
|
"prepares",
|
||||||
|
"screen",
|
||||||
|
"security",
|
||||||
|
"sleep",
|
||||||
|
"suspend",
|
||||||
|
"system"
|
||||||
|
],
|
||||||
|
"description": "Automatically lock the screen when the system prepares to suspend"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "lockScreenShowPasswordField",
|
"section": "lockScreenShowPasswordField",
|
||||||
"label": "Show Password Field",
|
"label": "Show Password Field",
|
||||||
@@ -2928,6 +3002,7 @@
|
|||||||
"playback",
|
"playback",
|
||||||
"player",
|
"player",
|
||||||
"progress",
|
"progress",
|
||||||
|
"scroll",
|
||||||
"settings",
|
"settings",
|
||||||
"spotify",
|
"spotify",
|
||||||
"statusbar",
|
"statusbar",
|
||||||
@@ -2938,6 +3013,24 @@
|
|||||||
"icon": "music_note",
|
"icon": "music_note",
|
||||||
"description": "Use animated wave progress bars for media playback"
|
"description": "Use animated wave progress bars for media playback"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "audioScrollMode",
|
||||||
|
"label": "Scroll Wheel",
|
||||||
|
"tabIndex": 16,
|
||||||
|
"category": "Media Player",
|
||||||
|
"keywords": [
|
||||||
|
"behavior",
|
||||||
|
"media",
|
||||||
|
"mpris",
|
||||||
|
"music",
|
||||||
|
"player",
|
||||||
|
"scroll",
|
||||||
|
"spotify",
|
||||||
|
"wheel",
|
||||||
|
"widget"
|
||||||
|
],
|
||||||
|
"description": "Scroll wheel behavior on media widget"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "notificationTimeoutCritical",
|
"section": "notificationTimeoutCritical",
|
||||||
"label": "Critical Priority",
|
"label": "Critical Priority",
|
||||||
@@ -3413,27 +3506,6 @@
|
|||||||
"icon": "schedule",
|
"icon": "schedule",
|
||||||
"description": "Gradually fade the screen before locking with a configurable grace period"
|
"description": "Gradually fade the screen before locking with a configurable grace period"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"section": "lockBeforeSuspend",
|
|
||||||
"label": "Lock before suspend",
|
|
||||||
"tabIndex": 21,
|
|
||||||
"category": "Power & Sleep",
|
|
||||||
"keywords": [
|
|
||||||
"automatically",
|
|
||||||
"before",
|
|
||||||
"energy",
|
|
||||||
"lock",
|
|
||||||
"power",
|
|
||||||
"prepares",
|
|
||||||
"screen",
|
|
||||||
"security",
|
|
||||||
"shutdown",
|
|
||||||
"sleep",
|
|
||||||
"suspend",
|
|
||||||
"system"
|
|
||||||
],
|
|
||||||
"description": "Automatically lock the screen when the system prepares to suspend"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"section": "powerConfirmation",
|
"section": "powerConfirmation",
|
||||||
"label": "Power Action Confirmation",
|
"label": "Power Action Confirmation",
|
||||||
|
|||||||
Reference in New Issue
Block a user