1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-02 10:32:07 -04:00

Compare commits

...

9 Commits

Author SHA1 Message Date
purian23
6238e065f2 distros: Workflows input type updates 2026-02-24 23:24:05 -05:00
purian23
72fbbfdd0d distros: Update workflows 2026-02-24 22:59:17 -05:00
purian23
2796c1cd4d fix: Defer DankCircularImage saving until the window is available 2026-02-24 21:23:36 -05:00
bbedward
54c9886627 settings: make horizontal change more smart 2026-02-24 20:48:42 -05:00
bbedward
05713cb389 settings: restore notifyHorizontalBarChanged 2026-02-24 19:42:29 -05:00
purian23
8bb3ee5f18 fix: Update HTML rendering injections 2026-02-24 19:34:46 -05:00
purian23
bc0b4825f1 dbar: Refactor to memoize dbar & widget state via json 2026-02-24 18:56:30 -05:00
purian23
ef7f17abf4 cpu widget: Fix monitor binding 2026-02-24 17:21:42 -05:00
Yamada.Kazuyoshi
876cd21f0b display battery consumption / charging W (#1814) 2026-02-24 15:42:47 -05:00
13 changed files with 257 additions and 132 deletions

View File

@@ -9,8 +9,8 @@ on:
type: choice
options:
- dms
- dms-git
- dms-greeter
- dms-git
- all
default: "dms"
rebuild_release:
@@ -119,9 +119,8 @@ jobs:
echo "🔄 Manual rebuild requested: $PKG (db$REBUILD)"
elif [[ "$PKG" == "all" ]]; then
# Check each package and build list of those needing updates
# Check each stable package and build list of those needing updates
PACKAGES_TO_UPDATE=()
check_dms_git && PACKAGES_TO_UPDATE+=("dms-git")
if check_dms_stable; then
PACKAGES_TO_UPDATE+=("dms")
if [[ -n "$LATEST_TAG" ]]; then
@@ -140,7 +139,7 @@ jobs:
else
echo "packages=" >> $GITHUB_OUTPUT
echo "has_updates=false" >> $GITHUB_OUTPUT
echo "✓ All packages up to date"
echo "✓ Both packages up to date"
fi
elif [[ "$PKG" == "dms-git" ]]; then
@@ -245,7 +244,7 @@ jobs:
fi
- name: Update dms-git spec version
if: contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all'
if: contains(steps.packages.outputs.packages, 'dms-git')
run: |
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
COMMIT_COUNT=$(git rev-list --count HEAD)
@@ -266,7 +265,7 @@ jobs:
} > distro/opensuse/dms-git.spec
- name: Update Debian dms-git changelog version
if: contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all'
if: contains(steps.packages.outputs.packages, 'dms-git')
run: |
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
COMMIT_COUNT=$(git rev-list --count HEAD)
@@ -389,7 +388,7 @@ jobs:
UPLOADED_PACKAGES=()
SKIPPED_PACKAGES=()
# PACKAGES can be space-separated list (e.g., "dms-git dms" from "all" check)
# PACKAGES can be space-separated list (e.g., "dms dms-greeter" from "all" check)
# Loop through each package and upload
for PKG in $PACKAGES; do
echo ""

View File

@@ -4,9 +4,15 @@ on:
workflow_dispatch:
inputs:
package:
description: "Package to upload (dms, dms-git, dms-greeter, or all)"
required: false
default: "dms-git"
description: "Package to upload"
required: true
type: choice
options:
- dms
- dms-greeter
- dms-git
- all
default: "dms"
rebuild_release:
description: "Release number for rebuilds (e.g., 2, 3, 4 for ppa2, ppa3, ppa4)"
required: false
@@ -139,7 +145,7 @@ jobs:
fi
else
# Fallback
echo "packages=dms-git" >> $GITHUB_OUTPUT
echo "packages=dms" >> $GITHUB_OUTPUT
echo "has_updates=true" >> $GITHUB_OUTPUT
fi
@@ -209,7 +215,7 @@ jobs:
echo "✓ Using rebuild release number: ppa$REBUILD_RELEASE"
fi
# PACKAGES can be space-separated list (e.g., "dms-git dms" from "all" check)
# PACKAGES can be space-separated list (e.g., "dms-git dms dms-greeter" from "all" check)
# Loop through each package and upload
for PKG in $PACKAGES; do
# Map package to PPA name

View File

@@ -3,7 +3,7 @@
<service name="download_url">
<param name="protocol">https</param>
<param name="host">github.com</param>
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.4.2/dms-qml.tar.gz</param>
<param name="path">/AvengeMedia/DankMaterialShell/releases/download/v1.4.3/dms-qml.tar.gz</param>
<param name="filename">dms-qml.tar.gz</param>
</service>
</services>

View File

@@ -1,6 +1,5 @@
dms-greeter (1.4.2db8) unstable; urgency=medium
dms-greeter (1.4.3db1) unstable; urgency=medium
* Initial Debian OBS package
* Port from Ubuntu/Fedora packaging
* Update to v1.4.3 stable release
-- Avenge Media <AvengeMedia.US@gmail.com> Sat, 21 Feb 2026 00:00:00 +0000
-- Avenge Media <AvengeMedia.US@gmail.com> Tue, 25 Feb 2026 02:40:00 +0000

View File

@@ -419,6 +419,9 @@ if [[ "$UPLOAD_OPENSUSE" == true ]] && [[ -f "distro/opensuse/$PACKAGE.spec" ]];
sed -i "s/VERSION_PLACEHOLDER/${DMS_GREETER_BASE_VERSION}/g" "$WORK_DIR/$PACKAGE.spec"
sed -i "s/RELEASE_PLACEHOLDER/${DMS_GREETER_RELEASE}/g" "$WORK_DIR/$PACKAGE.spec"
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" "$WORK_DIR/$PACKAGE.spec"
# Explicitly set Version:/Release: in case the spec uses %{version} macro
sed -i "s/^Version:.*/Version: ${DMS_GREETER_BASE_VERSION}/" "$WORK_DIR/$PACKAGE.spec"
sed -i "s/^Release:.*/Release: ${DMS_GREETER_RELEASE}%{?dist}/" "$WORK_DIR/$PACKAGE.spec"
fi
if [[ -f "$WORK_DIR/.osc/$PACKAGE.spec" ]]; then
@@ -813,6 +816,9 @@ if [[ "$UPLOAD_DEBIAN" == true ]] && [[ -d "distro/debian/$PACKAGE/debian" ]]; t
sed -i "s/VERSION_PLACEHOLDER/${DMS_GREETER_BASE_VERSION}/g" "$WORK_DIR/$PACKAGE.spec"
sed -i "s/RELEASE_PLACEHOLDER/${DMS_GREETER_RELEASE}/g" "$WORK_DIR/$PACKAGE.spec"
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" "$WORK_DIR/$PACKAGE.spec"
# Explicitly set Version:/Release: in case the spec uses %{version} macro
sed -i "s/^Version:.*/Version: ${DMS_GREETER_BASE_VERSION}/" "$WORK_DIR/$PACKAGE.spec"
sed -i "s/^Release:.*/Release: ${DMS_GREETER_RELEASE}%{?dist}/" "$WORK_DIR/$PACKAGE.spec"
fi
fi

View File

@@ -152,21 +152,35 @@ Item {
}
}
property string _barLayoutStateJson: {
const configs = SettingsData.barConfigs;
const mapped = configs.map(c => ({
id: c.id,
position: c.position,
autoHide: c.autoHide,
visible: c.visible
})).sort((a, b) => {
const aVertical = a.position === SettingsData.Position.Left || a.position === SettingsData.Position.Right;
const bVertical = b.position === SettingsData.Position.Left || b.position === SettingsData.Position.Right;
if (aVertical !== bVertical) {
return aVertical - bVertical;
}
return String(a.id).localeCompare(String(b.id));
});
return JSON.stringify(mapped);
}
on_BarLayoutStateJsonChanged: {
if (typeof dockRecreateDebounce !== "undefined") {
dockRecreateDebounce.restart();
}
}
Repeater {
id: dankBarRepeater
model: ScriptModel {
id: barRepeaterModel
values: {
const configs = SettingsData.barConfigs;
return configs.map(c => ({
id: c.id,
position: c.position
})).sort((a, b) => {
const aVertical = a.position === SettingsData.Position.Left || a.position === SettingsData.Position.Right;
const bVertical = b.position === SettingsData.Position.Left || b.position === SettingsData.Position.Right;
return aVertical - bVertical;
});
}
values: JSON.parse(root._barLayoutStateJson)
}
property var hyprlandOverviewLoaderRef: hyprlandOverviewLoader
@@ -213,13 +227,6 @@ Item {
PolkitService.polkitAvailable;
}
Connections {
target: SettingsData
function onBarConfigsChanged() {
dockRecreateDebounce.restart();
}
}
Loader {
id: dockLoader
active: root.dockEnabled

View File

@@ -21,73 +21,82 @@ Item {
property alias centerWidgetsModel: centerWidgetsModel
property alias rightWidgetsModel: rightWidgetsModel
property string _leftWidgetsJson: {
root.barConfig;
const leftWidgets = root.barConfig?.leftWidgets || [];
const mapped = leftWidgets.map((w, index) => {
if (typeof w === "string") {
return {
widgetId: w,
id: w + "_" + index,
enabled: true
};
} else {
const obj = Object.assign({}, w);
obj.widgetId = w.id || w.widgetId;
obj.id = (w.id || w.widgetId) + "_" + index;
obj.enabled = w.enabled !== false;
return obj;
}
});
return JSON.stringify(mapped);
}
property string _centerWidgetsJson: {
root.barConfig;
const centerWidgets = root.barConfig?.centerWidgets || [];
const mapped = centerWidgets.map((w, index) => {
if (typeof w === "string") {
return {
widgetId: w,
id: w + "_" + index,
enabled: true
};
} else {
const obj = Object.assign({}, w);
obj.widgetId = w.id || w.widgetId;
obj.id = (w.id || w.widgetId) + "_" + index;
obj.enabled = w.enabled !== false;
return obj;
}
});
return JSON.stringify(mapped);
}
property string _rightWidgetsJson: {
root.barConfig;
const rightWidgets = root.barConfig?.rightWidgets || [];
const mapped = rightWidgets.map((w, index) => {
if (typeof w === "string") {
return {
widgetId: w,
id: w + "_" + index,
enabled: true
};
} else {
const obj = Object.assign({}, w);
obj.widgetId = w.id || w.widgetId;
obj.id = (w.id || w.widgetId) + "_" + index;
obj.enabled = w.enabled !== false;
return obj;
}
});
return JSON.stringify(mapped);
}
ScriptModel {
id: leftWidgetsModel
values: {
root.barConfig;
const leftWidgets = root.barConfig?.leftWidgets || [];
return leftWidgets.map((w, index) => {
if (typeof w === "string") {
return {
widgetId: w,
id: w + "_" + index,
enabled: true
};
} else {
const obj = Object.assign({}, w);
obj.widgetId = w.id || w.widgetId;
obj.id = (w.id || w.widgetId) + "_" + index;
obj.enabled = w.enabled !== false;
return obj;
}
});
}
values: JSON.parse(root._leftWidgetsJson)
}
ScriptModel {
id: centerWidgetsModel
values: {
root.barConfig;
const centerWidgets = root.barConfig?.centerWidgets || [];
return centerWidgets.map((w, index) => {
if (typeof w === "string") {
return {
widgetId: w,
id: w + "_" + index,
enabled: true
};
} else {
const obj = Object.assign({}, w);
obj.widgetId = w.id || w.widgetId;
obj.id = (w.id || w.widgetId) + "_" + index;
obj.enabled = w.enabled !== false;
return obj;
}
});
}
values: JSON.parse(root._centerWidgetsJson)
}
ScriptModel {
id: rightWidgetsModel
values: {
root.barConfig;
const rightWidgets = root.barConfig?.rightWidgets || [];
return rightWidgets.map((w, index) => {
if (typeof w === "string") {
return {
widgetId: w,
id: w + "_" + index,
enabled: true
};
} else {
const obj = Object.assign({}, w);
obj.widgetId = w.id || w.widgetId;
obj.id = (w.id || w.widgetId) + "_" + index;
obj.enabled = w.enabled !== false;
return obj;
}
});
}
values: JSON.parse(root._rightWidgetsJson)
}
function triggerControlCenterOnFocusedScreen() {

View File

@@ -167,9 +167,22 @@ DankPopout {
}
Column {
id: headerInfoColumn
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
width: parent.width - Theme.iconSizeLarge - 32 - Theme.spacingM * 2
readonly property string timeInfoText: {
if (!BatteryService.batteryAvailable)
return "Power profile management available";
const time = BatteryService.formatTimeRemaining();
if (time !== "Unknown") {
return BatteryService.isCharging ? `Time until full: ${time}` : `Time remaining: ${time}`;
}
return "";
}
readonly property bool showPowerRate: BatteryService.batteryAvailable && Math.abs(BatteryService.changeRate) > 0.05
readonly property bool isOnAC: BatteryService.batteryAvailable && (BatteryService.isCharging || BatteryService.isPluggedIn)
readonly property bool isDischarging: BatteryService.batteryAvailable && !BatteryService.isCharging && !BatteryService.isPluggedIn
Row {
spacing: Theme.spacingS
@@ -207,21 +220,35 @@ DankPopout {
}
}
StyledText {
text: {
if (!BatteryService.batteryAvailable)
return "Power profile management available";
const time = BatteryService.formatTimeRemaining();
if (time !== "Unknown") {
return BatteryService.isCharging ? `Time until full: ${time}` : `Time remaining: ${time}`;
}
return "";
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
visible: text.length > 0
elide: Text.ElideRight
Row {
width: parent.width
spacing: Theme.spacingS
visible: headerInfoColumn.timeInfoText.length > 0
StyledText {
id: powerRateText
text: `${headerInfoColumn.isOnAC ? "+" : (headerInfoColumn.isDischarging ? "-" : "")}${Math.abs(BatteryService.changeRate).toFixed(1)}W`
font.pixelSize: Theme.fontSizeSmall
color: {
if (headerInfoColumn.isOnAC) {
return Theme.primary;
}
if (headerInfoColumn.isDischarging) {
return Theme.warning;
}
return Theme.surfaceTextMedium;
}
font.weight: Font.Medium
visible: headerInfoColumn.showPowerRate
}
StyledText {
text: headerInfoColumn.timeInfoText
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
elide: Text.ElideRight
width: parent.width - (powerRateText.visible ? (powerRateText.implicitWidth + parent.spacing) : 0)
}
}
}

View File

@@ -93,7 +93,7 @@ BasePill {
id: textBox
anchors.verticalCenter: parent.verticalCenter
implicitWidth: root.minimumWidth ? Math.max(cpuBaseline.width, cpuText.paintedWidth) : cpuText.paintedWidth
implicitWidth: root.minimumWidth ? Math.max(cpuBaseline.width, cpuCurrent.width) : cpuCurrent.width
implicitHeight: cpuText.implicitHeight
width: implicitWidth
@@ -105,6 +105,12 @@ BasePill {
text: "88%"
}
StyledTextMetrics {
id: cpuCurrent
font.pixelSize: Theme.barTextSize(root.barThickness, root.barConfig?.fontScale, root.barConfig?.maximizeWidgetText)
text: cpuText.text
}
StyledText {
id: cpuText
text: {

View File

@@ -51,9 +51,34 @@ Item {
}
}
function _isBarActive(c) {
if (!c.enabled) return false;
const prefs = c.screenPreferences || ["all"];
if (prefs.length > 0) return true;
return (c.showOnLastDisplay ?? true) && Quickshell.screens.length === 1;
}
function notifyHorizontalBarChange() {
if (selectedBarIsVertical)
const configs = SettingsData.barConfigs;
if (configs.length < 2)
return;
const hasHorizontal = configs.some(c => {
if (!_isBarActive(c)) return false;
const p = c.position ?? SettingsData.Position.Top;
return p === SettingsData.Position.Top || p === SettingsData.Position.Bottom;
});
if (!hasHorizontal)
return;
const hasVertical = configs.some(c => {
if (!_isBarActive(c)) return false;
const p = c.position ?? SettingsData.Position.Top;
return p === SettingsData.Position.Left || p === SettingsData.Position.Right;
});
if (!hasVertical)
return;
horizontalBarChangeDebounce.restart();
}
@@ -147,6 +172,7 @@ Item {
SettingsData.updateBarConfig(barId, {
screenPreferences: prefs
});
notifyHorizontalBarChange();
}
function getBarShowOnLastDisplay(barId) {
@@ -158,6 +184,8 @@ Item {
SettingsData.updateBarConfig(barId, {
showOnLastDisplay: value
});
if (Quickshell.screens.length === 1)
notifyHorizontalBarChange();
}
DankFlickable {
@@ -539,13 +567,10 @@ Item {
newPos = SettingsData.Position.Right;
break;
}
const wasVertical = selectedBarIsVertical;
SettingsData.updateBarConfig(selectedBarId, {
position: newPos
});
const isVertical = newPos === SettingsData.Position.Left || newPos === SettingsData.Position.Right;
if (wasVertical !== isVertical || !isVertical)
notifyHorizontalBarChange();
notifyHorizontalBarChange();
}
}
}
@@ -923,9 +948,9 @@ Item {
value: Math.round((selectedBarConfig?.fontScale ?? 1.0) * 100)
unit: "%"
defaultValue: 100
onSliderDragFinished: finalValue => {
onSliderValueChanged: newValue => {
SettingsData.updateBarConfig(selectedBarId, {
fontScale: finalValue / 100
fontScale: newValue / 100
});
}
@@ -948,9 +973,9 @@ Item {
value: Math.round((selectedBarConfig?.iconScale ?? 1.0) * 100)
unit: "%"
defaultValue: 100
onSliderDragFinished: finalValue => {
onSliderValueChanged: newValue => {
SettingsData.updateBarConfig(selectedBarId, {
iconScale: finalValue / 100
iconScale: newValue / 100
});
}

View File

@@ -18,6 +18,8 @@ Singleton {
function registerWidget(widgetId, screenName, widgetRef) {
if (!widgetId || !screenName || !widgetRef)
return;
if (typeof widgetRegistry !== "object" || widgetRegistry === null)
widgetRegistry = ({});
if (!widgetRegistry[widgetId])
widgetRegistry[widgetId] = {};
@@ -29,6 +31,8 @@ Singleton {
function unregisterWidget(widgetId, screenName) {
if (!widgetId || !screenName)
return;
if (typeof widgetRegistry !== "object" || widgetRegistry === null)
return;
if (!widgetRegistry[widgetId])
return;

View File

@@ -158,7 +158,10 @@ Singleton {
continue;
const urg = typeof item.urgency === "number" ? item.urgency : 1;
const body = item.body || "";
const htmlBody = item.htmlBody || _resolveHtmlBody(body);
let htmlBody = item.htmlBody || _resolveHtmlBody(body);
if (htmlBody) {
htmlBody = htmlBody.replace(/<img\b[^>]*>/gi, "");
}
loaded.push({
id: item.id || "",
summary: item.summary || "",
@@ -720,8 +723,8 @@ Singleton {
}
required property Notification notification
readonly property string summary: notification?.summary ?? ""
readonly property string body: notification?.body ?? ""
readonly property string summary: (notification?.summary ?? "").replace(/<img\b[^>]*>/gi, "")
readonly property string body: (notification?.body ?? "").replace(/<img\b[^>]*>/gi, "")
readonly property string htmlBody: root._resolveHtmlBody(body)
readonly property string appIcon: notification?.appIcon ?? ""
readonly property string appName: {
@@ -978,22 +981,34 @@ Singleton {
function _resolveHtmlBody(body) {
if (!body)
return "";
if (/<\/?[a-z][\s\S]*>/i.test(body))
return body;
// Decode percent-encoded URLs (e.g. https%3A%2F%2F → https://)
body = body.replace(/\bhttps?%3A%2F%2F[^\s]+/gi, match => {
try { return decodeURIComponent(match); }
catch (e) { return match; }
});
let result = body;
if (/&(#\d+|#x[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]+);/.test(body)) {
const decoded = _decodeEntities(body);
if (/<\/?[a-z][\s\S]*>/i.test(decoded))
return decoded;
return Markdown2Html.markdownToHtml(decoded);
if (/<\/?[a-z][\s\S]*>/i.test(body)) {
result = body;
} else {
// Decode percent-encoded URLs (e.g. https%3A%2F%2F → https://)
let processed = body.replace(/\bhttps?%3A%2F%2F[^\s]+/gi, match => {
try {
return decodeURIComponent(match);
} catch (e) {
return match;
}
});
if (/&(#\d+|#x[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]+);/.test(processed)) {
const decoded = _decodeEntities(processed);
if (/<\/?[a-z][\s\S]*>/i.test(decoded))
result = decoded;
else
result = Markdown2Html.markdownToHtml(decoded);
} else {
result = Markdown2Html.markdownToHtml(processed);
}
}
return Markdown2Html.markdownToHtml(body);
// Strip out image tags to prevent IP tracking
return result.replace(/<img\b[^>]*>/gi, "");
}
function getGroupKey(wrapper) {

View File

@@ -1,4 +1,5 @@
import QtQuick
import QtQuick.Window
import QtQuick.Effects
import qs.Common
import qs.Widgets
@@ -18,9 +19,30 @@ Rectangle {
signal imageSaved(string filePath)
property string _pendingSavePath: ""
property var _attachedWindow: root.Window.window
on_AttachedWindowChanged: {
if (_attachedWindow && _pendingSavePath !== "") {
Qt.callLater(function () {
if (root._pendingSavePath !== "") {
let path = root._pendingSavePath;
root._pendingSavePath = "";
root.saveImageToFile(path);
}
});
}
}
function saveImageToFile(filePath) {
if (activeImage.status !== Image.Ready)
return false;
if (!activeImage.Window.window) {
_pendingSavePath = filePath;
return true;
}
activeImage.grabToImage(function (result) {
if (result && result.saveToFile(filePath)) {
root.imageSaved(filePath);