mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
Compare commits
4 Commits
d23fc9f2df
...
c1d95a3086
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1d95a3086 | ||
|
|
9b027df1d5 | ||
|
|
5e03afe7f0 | ||
|
|
145a974b6d |
@@ -17,3 +17,4 @@ This file is more of a quick reference so I know what to account for before next
|
||||
- Theme registry
|
||||
- Notification persistence & history
|
||||
- **BREAKING** vscode theme needs re-installed
|
||||
- dms doctor cmd
|
||||
|
||||
@@ -147,6 +147,7 @@ func (c category) String() string {
|
||||
|
||||
const (
|
||||
checkNameMaxLength = 21
|
||||
doctorDocsURL = "https://danklinux.com/docs/dankmaterialshell/cli-doctor"
|
||||
)
|
||||
|
||||
type checkResult struct {
|
||||
@@ -155,6 +156,7 @@ type checkResult struct {
|
||||
status status
|
||||
message string
|
||||
details string
|
||||
url string
|
||||
}
|
||||
|
||||
type checkResultJSON struct {
|
||||
@@ -163,6 +165,7 @@ type checkResultJSON struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Details string `json:"details,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type doctorOutputJSON struct {
|
||||
@@ -182,6 +185,7 @@ func (r checkResult) toJSON() checkResultJSON {
|
||||
Status: string(r.status),
|
||||
Message: r.message,
|
||||
Details: r.details,
|
||||
URL: r.url,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,20 +235,21 @@ func checkSystemInfo() []checkResult {
|
||||
|
||||
if strings.Contains(err.Error(), "Unsupported distribution") {
|
||||
osRelease := readOSRelease()
|
||||
if osRelease["ID"] == "nixos" {
|
||||
switch {
|
||||
case osRelease["ID"] == "nixos":
|
||||
status = statusOK
|
||||
message = osRelease["PRETTY_NAME"]
|
||||
if message == "" {
|
||||
message = fmt.Sprintf("NixOS %s", osRelease["VERSION_ID"])
|
||||
}
|
||||
details = "Supported for runtime (install via NixOS module or Flake)"
|
||||
} else if osRelease["PRETTY_NAME"] != "" {
|
||||
case osRelease["PRETTY_NAME"] != "":
|
||||
message = fmt.Sprintf("%s (not supported by dms setup)", osRelease["PRETTY_NAME"])
|
||||
details = "DMS may work but automatic installation is not available"
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, checkResult{catSystem, "Operating System", status, message, details})
|
||||
results = append(results, checkResult{catSystem, "Operating System", status, message, details, doctorDocsURL + "#operating-system"})
|
||||
} else {
|
||||
status := statusOK
|
||||
message := osInfo.PrettyName
|
||||
@@ -258,6 +263,7 @@ func checkSystemInfo() []checkResult {
|
||||
results = append(results, checkResult{
|
||||
catSystem, "Operating System", status, message,
|
||||
fmt.Sprintf("ID: %s, Version: %s, Arch: %s", osInfo.Distribution.ID, osInfo.VersionID, osInfo.Architecture),
|
||||
doctorDocsURL + "#operating-system",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -266,7 +272,7 @@ func checkSystemInfo() []checkResult {
|
||||
if arch != "amd64" && arch != "arm64" {
|
||||
archStatus = statusError
|
||||
}
|
||||
results = append(results, checkResult{catSystem, "Architecture", archStatus, arch, ""})
|
||||
results = append(results, checkResult{catSystem, "Architecture", archStatus, arch, "", doctorDocsURL + "#architecture"})
|
||||
|
||||
waylandDisplay := os.Getenv("WAYLAND_DISPLAY")
|
||||
xdgSessionType := os.Getenv("XDG_SESSION_TYPE")
|
||||
@@ -276,13 +282,15 @@ func checkSystemInfo() []checkResult {
|
||||
results = append(results, checkResult{
|
||||
catSystem, "Display Server", statusOK, "Wayland",
|
||||
fmt.Sprintf("WAYLAND_DISPLAY=%s", waylandDisplay),
|
||||
doctorDocsURL + "#display-server",
|
||||
})
|
||||
case xdgSessionType == "x11":
|
||||
results = append(results, checkResult{catSystem, "Display Server", statusError, "X11 (DMS requires Wayland)", ""})
|
||||
results = append(results, checkResult{catSystem, "Display Server", statusError, "X11 (DMS requires Wayland)", "", doctorDocsURL + "#display-server"})
|
||||
default:
|
||||
results = append(results, checkResult{
|
||||
catSystem, "Display Server", statusWarn, "Unknown (ensure you're running Wayland)",
|
||||
fmt.Sprintf("XDG_SESSION_TYPE=%s", xdgSessionType),
|
||||
doctorDocsURL + "#display-server",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -299,9 +307,10 @@ func checkEnvironmentVars() []checkResult {
|
||||
func checkEnvVar(name string) []checkResult {
|
||||
value := os.Getenv(name)
|
||||
if value != "" {
|
||||
return []checkResult{{catEnvironment, name, statusInfo, value, ""}}
|
||||
} else if doctorVerbose {
|
||||
return []checkResult{{catEnvironment, name, statusInfo, "Not set", ""}}
|
||||
return []checkResult{{catEnvironment, name, statusInfo, value, "", doctorDocsURL + "#environment-variables"}}
|
||||
}
|
||||
if doctorVerbose {
|
||||
return []checkResult{{catEnvironment, name, statusInfo, "Not set", "", doctorDocsURL + "#environment-variables"}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -328,7 +337,7 @@ func checkVersions(qsMissingFeatures bool) []checkResult {
|
||||
}
|
||||
|
||||
results := []checkResult{
|
||||
{catVersions, "DMS CLI", statusOK, formatVersion(Version), dmsCliDetails},
|
||||
{catVersions, "DMS CLI", statusOK, formatVersion(Version), dmsCliDetails, doctorDocsURL + "#dms-cli"},
|
||||
}
|
||||
|
||||
qsVersion, qsStatus, qsPath := getQuickshellVersionInfo(qsMissingFeatures)
|
||||
@@ -336,13 +345,13 @@ func checkVersions(qsMissingFeatures bool) []checkResult {
|
||||
if doctorVerbose && qsPath != "" {
|
||||
qsDetails = qsPath
|
||||
}
|
||||
results = append(results, checkResult{catVersions, "Quickshell", qsStatus, qsVersion, qsDetails})
|
||||
results = append(results, checkResult{catVersions, "Quickshell", qsStatus, qsVersion, qsDetails, doctorDocsURL + "#quickshell"})
|
||||
|
||||
dmsVersion, dmsPath := getDMSShellVersion()
|
||||
if dmsVersion != "" {
|
||||
results = append(results, checkResult{catVersions, "DMS Shell", statusOK, dmsVersion, dmsPath})
|
||||
results = append(results, checkResult{catVersions, "DMS Shell", statusOK, dmsVersion, dmsPath, doctorDocsURL + "#dms-shell"})
|
||||
} else {
|
||||
results = append(results, checkResult{catVersions, "DMS Shell", statusError, "Not installed or not detected", "Run 'dms setup' to install"})
|
||||
results = append(results, checkResult{catVersions, "DMS Shell", statusError, "Not installed or not detected", "Run 'dms setup' to install", doctorDocsURL + "#dms-shell"})
|
||||
}
|
||||
|
||||
return results
|
||||
@@ -405,16 +414,16 @@ func checkDMSInstallation() []checkResult {
|
||||
}
|
||||
|
||||
if dmsPath == "" {
|
||||
return []checkResult{{catInstallation, "DMS Configuration", statusError, "Not found", "shell.qml not found in any config path"}}
|
||||
return []checkResult{{catInstallation, "DMS Configuration", statusError, "Not found", "shell.qml not found in any config path", doctorDocsURL + "#dms-configuration"}}
|
||||
}
|
||||
|
||||
results = append(results, checkResult{catInstallation, "DMS Configuration", statusOK, "Found", dmsPath})
|
||||
results = append(results, checkResult{catInstallation, "DMS Configuration", statusOK, "Found", dmsPath, doctorDocsURL + "#dms-configuration"})
|
||||
|
||||
shellQml := filepath.Join(dmsPath, "shell.qml")
|
||||
if _, err := os.Stat(shellQml); err != nil {
|
||||
results = append(results, checkResult{catInstallation, "shell.qml", statusError, "Missing", shellQml})
|
||||
results = append(results, checkResult{catInstallation, "shell.qml", statusError, "Missing", shellQml, doctorDocsURL + "#dms-configuration"})
|
||||
} else {
|
||||
results = append(results, checkResult{catInstallation, "shell.qml", statusOK, "Present", shellQml})
|
||||
results = append(results, checkResult{catInstallation, "shell.qml", statusOK, "Present", shellQml, doctorDocsURL + "#dms-configuration"})
|
||||
}
|
||||
|
||||
if doctorVerbose {
|
||||
@@ -427,7 +436,7 @@ func checkDMSInstallation() []checkResult {
|
||||
case strings.Contains(dmsPath, ".config"):
|
||||
installType = "User config"
|
||||
}
|
||||
results = append(results, checkResult{catInstallation, "Install Type", statusInfo, installType, dmsPath})
|
||||
results = append(results, checkResult{catInstallation, "Install Type", statusInfo, installType, dmsPath, doctorDocsURL + "#dms-configuration"})
|
||||
}
|
||||
|
||||
return results
|
||||
@@ -450,24 +459,26 @@ func checkWindowManagers() []checkResult {
|
||||
foundAny := false
|
||||
|
||||
for _, c := range compositors {
|
||||
if slices.ContainsFunc(c.commands, utils.CommandExists) {
|
||||
foundAny = true
|
||||
var compositorPath string
|
||||
for _, cmd := range c.commands {
|
||||
if path, err := exec.LookPath(cmd); err == nil {
|
||||
compositorPath = path
|
||||
break
|
||||
}
|
||||
}
|
||||
details := ""
|
||||
if doctorVerbose && compositorPath != "" {
|
||||
details = compositorPath
|
||||
}
|
||||
results = append(results, checkResult{
|
||||
catCompositor, c.name, statusOK,
|
||||
getVersionFromCommand(c.versionCmd, c.versionArg, c.versionRegex), details,
|
||||
})
|
||||
if !slices.ContainsFunc(c.commands, utils.CommandExists) {
|
||||
continue
|
||||
}
|
||||
foundAny = true
|
||||
var compositorPath string
|
||||
for _, cmd := range c.commands {
|
||||
if path, err := exec.LookPath(cmd); err == nil {
|
||||
compositorPath = path
|
||||
break
|
||||
}
|
||||
}
|
||||
details := ""
|
||||
if doctorVerbose && compositorPath != "" {
|
||||
details = compositorPath
|
||||
}
|
||||
results = append(results, checkResult{
|
||||
catCompositor, c.name, statusOK,
|
||||
getVersionFromCommand(c.versionCmd, c.versionArg, c.versionRegex), details,
|
||||
doctorDocsURL + "#compositor",
|
||||
})
|
||||
}
|
||||
|
||||
if !foundAny {
|
||||
@@ -475,11 +486,12 @@ func checkWindowManagers() []checkResult {
|
||||
catCompositor, "Compositor", statusError,
|
||||
"No supported Wayland compositor found",
|
||||
"Install Hyprland, niri, Sway, River, or Wayfire",
|
||||
doctorDocsURL + "#compositor",
|
||||
})
|
||||
}
|
||||
|
||||
if wm := detectRunningWM(); wm != "" {
|
||||
results = append(results, checkResult{catCompositor, "Active", statusInfo, wm, ""})
|
||||
results = append(results, checkResult{catCompositor, "Active", statusInfo, wm, "", doctorDocsURL + "#compositor"})
|
||||
}
|
||||
|
||||
return results
|
||||
@@ -601,7 +613,7 @@ ShellRoot {
|
||||
status, message = statusInfo, "Not available"
|
||||
missingFeatures = true
|
||||
}
|
||||
results = append(results, checkResult{catQuickshellFeatures, f.name, status, message, f.desc})
|
||||
results = append(results, checkResult{catQuickshellFeatures, f.name, status, message, f.desc, doctorDocsURL + "#quickshell-features"})
|
||||
}
|
||||
|
||||
return results, missingFeatures
|
||||
@@ -610,16 +622,16 @@ ShellRoot {
|
||||
func checkI2CAvailability() checkResult {
|
||||
ddc, err := brightness.NewDDCBackend()
|
||||
if err != nil {
|
||||
return checkResult{catOptionalFeatures, "I2C/DDC", statusInfo, "Not available", "External monitor brightness control"}
|
||||
return checkResult{catOptionalFeatures, "I2C/DDC", statusInfo, "Not available", "External monitor brightness control", doctorDocsURL + "#optional-features"}
|
||||
}
|
||||
defer ddc.Close()
|
||||
|
||||
devices, err := ddc.GetDevices()
|
||||
if err != nil || len(devices) == 0 {
|
||||
return checkResult{catOptionalFeatures, "I2C/DDC", statusInfo, "No monitors detected", "External monitor brightness control"}
|
||||
return checkResult{catOptionalFeatures, "I2C/DDC", statusInfo, "No monitors detected", "External monitor brightness control", doctorDocsURL + "#optional-features"}
|
||||
}
|
||||
|
||||
return checkResult{catOptionalFeatures, "I2C/DDC", statusOK, fmt.Sprintf("%d monitor(s) detected", len(devices)), "External monitor brightness control"}
|
||||
return checkResult{catOptionalFeatures, "I2C/DDC", statusOK, fmt.Sprintf("%d monitor(s) detected", len(devices)), "External monitor brightness control", doctorDocsURL + "#optional-features"}
|
||||
}
|
||||
|
||||
func detectNetworkBackend() string {
|
||||
@@ -649,25 +661,24 @@ func checkOptionalDependencies() []checkResult {
|
||||
var results []checkResult
|
||||
|
||||
if utils.IsServiceActive("accounts-daemon", false) {
|
||||
results = append(results, checkResult{catOptionalFeatures, "accountsservice", statusOK, "Running", "User accounts"})
|
||||
results = append(results, checkResult{catOptionalFeatures, "accountsservice", statusOK, "Running", "User accounts", doctorDocsURL + "#optional-features"})
|
||||
} else {
|
||||
results = append(results, checkResult{catOptionalFeatures, "accountsservice", statusWarn, "Not running", "User accounts"})
|
||||
results = append(results, checkResult{catOptionalFeatures, "accountsservice", statusWarn, "Not running", "User accounts", doctorDocsURL + "#optional-features"})
|
||||
}
|
||||
|
||||
if utils.IsServiceActive("power-profiles-daemon", false) {
|
||||
results = append(results, checkResult{catOptionalFeatures, "power-profiles-daemon", statusOK, "Running", "Power profile management"})
|
||||
results = append(results, checkResult{catOptionalFeatures, "power-profiles-daemon", statusOK, "Running", "Power profile management", doctorDocsURL + "#optional-features"})
|
||||
} else {
|
||||
results = append(results, checkResult{catOptionalFeatures, "power-profiles-daemon", statusInfo, "Not running", "Power profile management"})
|
||||
results = append(results, checkResult{catOptionalFeatures, "power-profiles-daemon", statusInfo, "Not running", "Power profile management", doctorDocsURL + "#optional-features"})
|
||||
}
|
||||
|
||||
i2cStatus := checkI2CAvailability()
|
||||
results = append(results, i2cStatus)
|
||||
results = append(results, checkI2CAvailability())
|
||||
|
||||
terminals := []string{"ghostty", "kitty", "alacritty", "foot", "wezterm"}
|
||||
if idx := slices.IndexFunc(terminals, utils.CommandExists); idx >= 0 {
|
||||
results = append(results, checkResult{catOptionalFeatures, "Terminal", statusOK, terminals[idx], ""})
|
||||
results = append(results, checkResult{catOptionalFeatures, "Terminal", statusOK, terminals[idx], "", doctorDocsURL + "#optional-features"})
|
||||
} else {
|
||||
results = append(results, checkResult{catOptionalFeatures, "Terminal", statusWarn, "None found", "Install ghostty, kitty, or alacritty"})
|
||||
results = append(results, checkResult{catOptionalFeatures, "Terminal", statusWarn, "None found", "Install ghostty, kitty, or alacritty", doctorDocsURL + "#optional-features"})
|
||||
}
|
||||
|
||||
deps := []struct {
|
||||
@@ -686,13 +697,12 @@ func checkOptionalDependencies() []checkResult {
|
||||
|
||||
for _, d := range deps {
|
||||
found, foundCmd := utils.CommandExists(d.cmd), d.cmd
|
||||
if !found && d.altCmd != "" {
|
||||
if utils.CommandExists(d.altCmd) {
|
||||
found, foundCmd = true, d.altCmd
|
||||
}
|
||||
if !found && d.altCmd != "" && utils.CommandExists(d.altCmd) {
|
||||
found, foundCmd = true, d.altCmd
|
||||
}
|
||||
|
||||
if found {
|
||||
switch {
|
||||
case found:
|
||||
message := "Installed"
|
||||
details := d.desc
|
||||
if d.name == "Network" {
|
||||
@@ -711,11 +721,11 @@ func checkOptionalDependencies() []checkResult {
|
||||
}
|
||||
}
|
||||
}
|
||||
results = append(results, checkResult{catOptionalFeatures, d.name, statusOK, message, details})
|
||||
} else if d.important {
|
||||
results = append(results, checkResult{catOptionalFeatures, d.name, statusWarn, "Missing", d.desc})
|
||||
} else {
|
||||
results = append(results, checkResult{catOptionalFeatures, d.name, statusInfo, "Not installed", d.desc})
|
||||
results = append(results, checkResult{catOptionalFeatures, d.name, statusOK, message, details, doctorDocsURL + "#optional-features"})
|
||||
case d.important:
|
||||
results = append(results, checkResult{catOptionalFeatures, d.name, statusWarn, "Missing", d.desc, doctorDocsURL + "#optional-features"})
|
||||
default:
|
||||
results = append(results, checkResult{catOptionalFeatures, d.name, statusInfo, "Not installed", d.desc, doctorDocsURL + "#optional-features"})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -738,19 +748,18 @@ func checkConfigurationFiles() []checkResult {
|
||||
var results []checkResult
|
||||
for _, cf := range configFiles {
|
||||
info, err := os.Stat(cf.path)
|
||||
if err == nil {
|
||||
status := statusOK
|
||||
message := "Present"
|
||||
|
||||
if info.Mode().Perm()&0200 == 0 {
|
||||
status = statusWarn
|
||||
message += " (read-only)"
|
||||
}
|
||||
|
||||
results = append(results, checkResult{catConfigFiles, cf.name, status, message, cf.path})
|
||||
} else {
|
||||
results = append(results, checkResult{catConfigFiles, cf.name, statusInfo, "Not yet created", cf.path})
|
||||
if err != nil {
|
||||
results = append(results, checkResult{catConfigFiles, cf.name, statusInfo, "Not yet created", cf.path, doctorDocsURL + "#config-files"})
|
||||
continue
|
||||
}
|
||||
|
||||
status := statusOK
|
||||
message := "Present"
|
||||
if info.Mode().Perm()&0200 == 0 {
|
||||
status = statusWarn
|
||||
message += " (read-only)"
|
||||
}
|
||||
results = append(results, checkResult{catConfigFiles, cf.name, status, message, cf.path, doctorDocsURL + "#config-files"})
|
||||
}
|
||||
return results
|
||||
}
|
||||
@@ -764,29 +773,31 @@ func checkSystemdServices() []checkResult {
|
||||
|
||||
dmsState := getServiceState("dms", true)
|
||||
if !dmsState.exists {
|
||||
results = append(results, checkResult{catServices, "dms.service", statusInfo, "Not installed", "Optional user service"})
|
||||
results = append(results, checkResult{catServices, "dms.service", statusInfo, "Not installed", "Optional user service", doctorDocsURL + "#services"})
|
||||
} else {
|
||||
status, message := statusOK, dmsState.enabled
|
||||
if dmsState.active != "" {
|
||||
message = fmt.Sprintf("%s, %s", dmsState.enabled, dmsState.active)
|
||||
}
|
||||
if dmsState.enabled == "disabled" {
|
||||
switch {
|
||||
case dmsState.enabled == "disabled":
|
||||
status, message = statusWarn, "Disabled"
|
||||
} else if dmsState.active == "failed" || dmsState.active == "inactive" {
|
||||
case dmsState.active == "failed" || dmsState.active == "inactive":
|
||||
status = statusError
|
||||
}
|
||||
results = append(results, checkResult{catServices, "dms.service", status, message, ""})
|
||||
results = append(results, checkResult{catServices, "dms.service", status, message, "", doctorDocsURL + "#services"})
|
||||
}
|
||||
|
||||
greetdState := getServiceState("greetd", false)
|
||||
if greetdState.exists {
|
||||
switch {
|
||||
case greetdState.exists:
|
||||
status := statusOK
|
||||
if greetdState.enabled == "disabled" {
|
||||
status = statusInfo
|
||||
}
|
||||
results = append(results, checkResult{catServices, "greetd", status, greetdState.enabled, ""})
|
||||
} else if doctorVerbose {
|
||||
results = append(results, checkResult{catServices, "greetd", statusInfo, "Not installed", "Optional greeter service"})
|
||||
results = append(results, checkResult{catServices, "greetd", status, greetdState.enabled, "", doctorDocsURL + "#services"})
|
||||
case doctorVerbose:
|
||||
results = append(results, checkResult{catServices, "greetd", statusInfo, "Not installed", "Optional greeter service", doctorDocsURL + "#services"})
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
@@ -829,7 +829,11 @@ Item {
|
||||
Connections {
|
||||
target: FirstLaunchService
|
||||
function onGreeterRequested() {
|
||||
greeterLoader.active = true;
|
||||
if (greeterLoader.active && greeterLoader.item) {
|
||||
greeterLoader.item.show();
|
||||
} else {
|
||||
greeterLoader.active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -894,6 +894,26 @@ Item {
|
||||
target: "clipboard"
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function open(): string {
|
||||
FirstLaunchService.showWelcome();
|
||||
return "WELCOME_OPEN_SUCCESS";
|
||||
}
|
||||
|
||||
function doctor(): string {
|
||||
FirstLaunchService.showDoctor();
|
||||
return "WELCOME_DOCTOR_SUCCESS";
|
||||
}
|
||||
|
||||
function page(pageNum: string): string {
|
||||
const num = parseInt(pageNum) || 0;
|
||||
FirstLaunchService.showGreeter(num);
|
||||
return `WELCOME_PAGE_SUCCESS: ${num}`;
|
||||
}
|
||||
|
||||
target: "welcome"
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function toggleOverlay(instanceId: string): string {
|
||||
if (!instanceId)
|
||||
|
||||
@@ -50,8 +50,8 @@ Rectangle {
|
||||
Column {
|
||||
anchors.left: statusIcon.right
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.right: categoryChip.visible ? categoryChip.left : parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.right: categoryChip.visible ? categoryChip.left : (urlButton.visible ? urlButton.left : parent.right)
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 1
|
||||
|
||||
@@ -76,8 +76,8 @@ Rectangle {
|
||||
|
||||
Rectangle {
|
||||
id: categoryChip
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.right: urlButton.visible ? urlButton.left : parent.right
|
||||
anchors.rightMargin: urlButton.visible ? Theme.spacingXS : Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: Math.round(Theme.fontSizeSmall * 1.67)
|
||||
width: categoryText.implicitWidth + Theme.spacingS
|
||||
@@ -93,4 +93,17 @@ Rectangle {
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
id: urlButton
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "open_in_new"
|
||||
iconSize: Theme.iconSize - 6
|
||||
buttonSize: 24
|
||||
visible: !!(root.resultData?.url)
|
||||
tooltipText: root.resultData?.url || ""
|
||||
onClicked: Qt.openUrlExternally(root.resultData.url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,12 @@ FloatingWindow {
|
||||
}
|
||||
|
||||
function show() {
|
||||
currentPage = 0;
|
||||
currentPage = FirstLaunchService.requestedStartPage || 0;
|
||||
visible = true;
|
||||
}
|
||||
|
||||
function showAtPage(page) {
|
||||
currentPage = page;
|
||||
visible = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,9 +114,13 @@ Item {
|
||||
const items = AppSearchService.getPluginItems(pluginCategory, "");
|
||||
emptyTriggerItems = emptyTriggerItems.concat(items);
|
||||
});
|
||||
apps = AppSearchService.applications.concat(emptyTriggerItems);
|
||||
// Add Core Apps
|
||||
const coreItems = AppSearchService.getCoreApps("");
|
||||
apps = AppSearchService.applications.concat(emptyTriggerItems).concat(coreItems);
|
||||
} else {
|
||||
apps = AppSearchService.getAppsInCategory(selectedCategory).slice(0, maxResults);
|
||||
const coreItems = AppSearchService.getCoreApps("").filter(app => app.categories.includes(selectedCategory));
|
||||
apps = apps.concat(coreItems);
|
||||
}
|
||||
} else {
|
||||
if (selectedCategory === allCategory) {
|
||||
@@ -129,7 +133,9 @@ Item {
|
||||
const items = AppSearchService.getPluginItems(pluginCategory, searchQuery);
|
||||
emptyTriggerItems = emptyTriggerItems.concat(items);
|
||||
});
|
||||
apps = apps.concat(emptyTriggerItems);
|
||||
|
||||
const coreItems = AppSearchService.getCoreApps(searchQuery);
|
||||
apps = apps.concat(emptyTriggerItems).concat(coreItems);
|
||||
} else {
|
||||
const categoryApps = AppSearchService.getAppsInCategory(selectedCategory);
|
||||
if (categoryApps.length > 0) {
|
||||
@@ -139,6 +145,9 @@ Item {
|
||||
} else {
|
||||
apps = [];
|
||||
}
|
||||
|
||||
const coreItems = AppSearchService.getCoreApps(searchQuery).filter(app => app.categories.includes(selectedCategory));
|
||||
apps = apps.concat(coreItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,7 +182,7 @@ Item {
|
||||
seenNames.add(itemKey);
|
||||
uniqueApps.push(app);
|
||||
|
||||
const isPluginItem = app.action !== undefined;
|
||||
const isPluginItem = app.isCore ? false : (app.action !== undefined);
|
||||
filteredModel.append({
|
||||
"name": app.name || "",
|
||||
"exec": app.execString || app.exec || app.action || "",
|
||||
@@ -181,6 +190,7 @@ Item {
|
||||
"comment": app.comment || "",
|
||||
"categories": app.categories || [],
|
||||
"isPlugin": isPluginItem,
|
||||
"isCore": app.isCore === true,
|
||||
"appIndex": uniqueApps.length - 1
|
||||
});
|
||||
}
|
||||
@@ -237,6 +247,12 @@ Item {
|
||||
|
||||
const actualApp = _uniqueApps[appData.appIndex];
|
||||
|
||||
if (appData.isCore) {
|
||||
AppSearchService.executeCoreApp(actualApp);
|
||||
appLaunched(appData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (appData.isPlugin) {
|
||||
const pluginId = getPluginIdForItem(actualApp);
|
||||
if (pluginId) {
|
||||
|
||||
@@ -713,6 +713,63 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: toolsSection.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
border.width: 0
|
||||
|
||||
Column {
|
||||
id: toolsSection
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "build"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Tools")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Show Welcome")
|
||||
iconName: "waving_hand"
|
||||
backgroundColor: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||
textColor: Theme.surfaceText
|
||||
onClicked: FirstLaunchService.showWelcome()
|
||||
}
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("System Check")
|
||||
iconName: "vital_signs"
|
||||
backgroundColor: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
|
||||
textColor: Theme.surfaceText
|
||||
onClicked: FirstLaunchService.showDoctor()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: `<a href="https://github.com/AvengeMedia/DankMaterialShell/blob/master/LICENSE" style="text-decoration:none; color:${Theme.surfaceVariantText};">MIT License</a>`
|
||||
|
||||
@@ -42,6 +42,68 @@ Singleton {
|
||||
_cachedCategories = null;
|
||||
}
|
||||
|
||||
readonly property var coreApps: [
|
||||
{
|
||||
name: "DMS Settings",
|
||||
icon: Qt.resolvedUrl("../assets/danklogo2.svg"),
|
||||
comment: "Manage DMS configuration",
|
||||
action: "ipc:settings",
|
||||
categories: ["Settings", "System"],
|
||||
isCore: true
|
||||
},
|
||||
{
|
||||
name: "DMS Notepad",
|
||||
icon: "material:description",
|
||||
comment: "Quick notes",
|
||||
action: "ipc:notepad",
|
||||
categories: ["Office", "Utility"],
|
||||
isCore: true
|
||||
},
|
||||
{
|
||||
name: "DMS System Monitor",
|
||||
icon: "material:monitor_heart",
|
||||
comment: "System monitor and process list",
|
||||
action: "ipc:processlist",
|
||||
categories: ["System", "Monitor"],
|
||||
isCore: true
|
||||
}
|
||||
]
|
||||
|
||||
function getCoreApps(query) {
|
||||
if (!query || query.length === 0) {
|
||||
return coreApps;
|
||||
}
|
||||
|
||||
const lowerQuery = query.toLowerCase();
|
||||
return coreApps.filter(app => {
|
||||
return app.name.toLowerCase().includes(lowerQuery) || app.comment.toLowerCase().includes(lowerQuery);
|
||||
});
|
||||
}
|
||||
|
||||
function executeCoreApp(app) {
|
||||
if (!app || !app.action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const actionParts = app.action.split(":");
|
||||
const actionType = actionParts[0];
|
||||
const actionTarget = actionParts[1];
|
||||
|
||||
if (actionType === "ipc") {
|
||||
if (actionTarget === "settings") {
|
||||
Quickshell.execDetached(["dms", "ipc", "call", "settings", "toggle"]);
|
||||
return true;
|
||||
} else if (actionTarget === "notepad") {
|
||||
Quickshell.execDetached(["dms", "ipc", "call", "notepad", "toggle"]);
|
||||
return true;
|
||||
} else if (actionTarget === "processlist") {
|
||||
Quickshell.execDetached(["dms", "ipc", "call", "processlist", "focusOrToggle"]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopEntries
|
||||
function onApplicationsChanged() {
|
||||
|
||||
@@ -17,12 +17,26 @@ Singleton {
|
||||
property bool isFirstLaunch: false
|
||||
property bool checkComplete: false
|
||||
property bool greeterDismissed: false
|
||||
property int requestedStartPage: 0
|
||||
|
||||
readonly property bool shouldShowGreeter: checkComplete && isFirstLaunch && !greeterDismissed
|
||||
|
||||
signal greeterRequested
|
||||
signal greeterCompleted
|
||||
|
||||
function showGreeter(startPage) {
|
||||
requestedStartPage = startPage || 0;
|
||||
greeterRequested();
|
||||
}
|
||||
|
||||
function showWelcome() {
|
||||
showGreeter(0);
|
||||
}
|
||||
|
||||
function showDoctor() {
|
||||
showGreeter(1);
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
checkFirstLaunch();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
@@ -54,7 +53,7 @@ Item {
|
||||
source: root.iconPath
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
visible: !root.isMaterial && !root.isUnicode && status === Image.Ready
|
||||
visible: !root.isMaterial && !root.isUnicode && root.iconPath !== "" && status === Image.Ready
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -65,7 +64,7 @@ Item {
|
||||
anchors.rightMargin: root.fallbackRightMargin
|
||||
anchors.topMargin: root.fallbackTopMargin
|
||||
anchors.bottomMargin: root.fallbackBottomMargin
|
||||
visible: !root.isMaterial && !root.isUnicode && iconImg.status !== Image.Ready
|
||||
visible: !root.isMaterial && !root.isUnicode && (root.iconPath === "" || iconImg.status !== Image.Ready)
|
||||
color: root.fallbackBackgroundColor
|
||||
radius: Theme.cornerRadius
|
||||
border.width: 0
|
||||
|
||||
@@ -45,7 +45,7 @@ Rectangle {
|
||||
width: computedIconSize
|
||||
height: computedIconSize
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
iconValue: model.icon && model.icon !== "" ? model.icon : model.startupClass
|
||||
iconValue: (model.icon && model.icon !== "") ? model.icon : ""
|
||||
iconSize: computedIconSize
|
||||
fallbackText: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
|
||||
materialIconSizeAdjustment: root.iconMaterialSizeAdjustment
|
||||
|
||||
@@ -46,7 +46,7 @@ Rectangle {
|
||||
width: root.iconSize
|
||||
height: root.iconSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconValue: model.icon && model.icon !== "" ? model.icon : model.startupClass
|
||||
iconValue: (model.icon && model.icon !== "") ? model.icon : ""
|
||||
iconSize: root.iconSize
|
||||
fallbackText: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
|
||||
iconMargins: root.iconMargins
|
||||
|
||||
@@ -102,12 +102,10 @@ Item {
|
||||
|
||||
property string text: ""
|
||||
|
||||
implicitWidth: Math.min(300, Math.max(120, textContent.implicitWidth + Theme.spacingM * 2))
|
||||
implicitHeight: textContent.implicitHeight + Theme.spacingS * 2
|
||||
width: implicitWidth
|
||||
height: implicitHeight
|
||||
|
||||
padding: 0
|
||||
leftPadding: Theme.spacingM
|
||||
rightPadding: Theme.spacingM
|
||||
topPadding: Theme.spacingS
|
||||
bottomPadding: Theme.spacingS
|
||||
closePolicy: Popup.NoAutoClose
|
||||
modal: false
|
||||
dim: false
|
||||
@@ -122,6 +120,7 @@ Item {
|
||||
contentItem: Text {
|
||||
id: textContent
|
||||
|
||||
width: Math.min(implicitWidth, 500)
|
||||
text: tooltip.text
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
|
||||
@@ -23,7 +23,7 @@ MouseArea {
|
||||
|
||||
Timer {
|
||||
id: hoverDelay
|
||||
interval: 1000
|
||||
interval: 400
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
tooltip.show(root.tooltipText, root, 0, 0, "bottom");
|
||||
|
||||
Reference in New Issue
Block a user