1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

Compare commits

...

4 Commits

Author SHA1 Message Date
bbedward
c1d95a3086 launcher: fix invalid icon rendering wrong icon 2026-01-04 22:58:20 -05:00
bbedward
9b027df1d5 doctor: add links to dr command 2026-01-04 22:44:19 -05:00
purian23
5e03afe7f0 feat: Implement DMS Core Persistent Apps 2026-01-04 22:33:50 -05:00
bbedward
145a974b6d welcome: add IPC targets and button on about page 2026-01-04 21:45:02 -05:00
15 changed files with 299 additions and 98 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}
}
}

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -53,7 +53,12 @@ FloatingWindow {
}
function show() {
currentPage = 0;
currentPage = FirstLaunchService.requestedStartPage || 0;
visible = true;
}
function showAtPage(page) {
currentPage = page;
visible = true;
}

View File

@@ -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) {

View File

@@ -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>`

View File

@@ -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() {

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -23,7 +23,7 @@ MouseArea {
Timer {
id: hoverDelay
interval: 1000
interval: 400
repeat: false
onTriggered: {
tooltip.show(root.tooltipText, root, 0, 0, "bottom");