Modified copies of DMS built-in widgets. The code is copied from DankMaterialShell's default widgets with specific customizations applied.
Last synced: 2026-10-20
Base commit: 3284acab (matugen: neovim template reads the dms config dir) — upstream/master tip
Repository: https://github.com/AvengeMedia/DankMaterialShell
Local clone lives at
~/code/_forks/DankMaterialShell(branchfeature/ddc-controls, HEAD8960b38e=upstream/master+ fork DDC commits;upstream/masterverified as an ancestor). The running build is current:~/.local/bin/dmsresolves to adms-shell-2.0.0+date=2026-10-20_1d9a63dstore path, and1d9a63dcontains the upstream tip. The Nix profile (/etc/profiles/per-user/srb/bin/dms) still ships only the Go CLI —~/.local/binhas to stay ahead of the profile onPATH.
180 upstream commits; seven touch forked widgets, all ported. Upstream rebranded to dux
(789a968c), so the shell's own app id is now dev.duxlabs.shell.
- All icon widgets —
789a968c: the "is this the shell's own window" check isPaths.isDmsAppId()(CustomFocusedApp, CustomRunningApps ×2, CustomWorkspaceSwitcher). The hardcodedorg.quickshell/com.danklinux.dmspair missed the newdev.duxlabs.shellid, so the shell's icon lost its recolour layer. - CustomMedia —
8be08999"bar: host edge click strips in the bar body": the track tooltip followsisMouseHovered(which now includes the bar-edge strip) instead of aHoverHandleron the content, and hides when the lyric line goes away.
Fix from before the sync (own commit): CustomMedia's lyric line is
DBarWidgets.MediaLyricLine through an aliased qs.Modules.DBar.Widgets import. The 2026-10-08
port used a bare MediaLyricLine, which doesn't resolve from the plugins dir.
- CustomRunningApps —
39b41fb8: the fallbackinnerPaddinginbarBoundsOffsetis2. - CustomSystemTrayBar —
8ff7ce2c(island tray slot), ported by a clean three-way merge: key pinning, env hiding, icon sources, sorting, reordering and thedbus-sendfallback now callTrayMenuManager(qs.Common), and the overflow popup grid and tray menu are upstream'sTrayOverflowGrid/TrayMenuContent(-450 lines). Keys keep theid/id::nformat. The menu's "Keep in Bar" branch still calls ourisAutoOverflowTrayItem(), which staysfalse. - CustomWorkspaceSwitcher —
534913dc: Shift+wheel counts as horizontal and passes through, inlined becauseWheelInput.jsis imported by a shell-relative path. The overview pill change (effectiveScreenName) is already covered byWorkspaceSwitcherModel.hiddenByOverview.
190 upstream commits. Upstream moved the bar from Modules/DankBar to Modules/DBar and renamed
its Dank* widgets to D* (ccbf1d38, 9ec48c4f, fd52e0af), so every widget shows up as
changed; diff the old path at the base against the new one.
First, a breakage fix. The running build already carries the rename, which left four
plugins referencing types that no longer exist (found by reading the build, not seen at runtime): qs.Modules.DankBar no longer exports BarMetrics or BarPillSurface
(CustomSystemTrayBar, CustomMedia), and DankContextMenu / DankIconButton have no
compatibility shim (CustomRunningApps, CustomMedia). The remaining Dank* names still load
through shims that log a deprecation error per plugin. All 17 affected files now use the D*
names, import qs.DCommon.Widgets next to qs.Widgets, and qs.Modules.DBar[.Widgets].
- CustomFocusedApp —
a8146d2c"bar: shrink flexible widgets before moving anything into overflow", adapted. Our own pill clamp (~100 lines walking the parent chain to measure the gap between bar sections, plus a pollingTimer) is gone. The pill declaresnaturalPrimarySize/minimumPrimarySizeand receivesallottedPrimarySize, and the bar's resolver shrinks it before anything collides.maxNormalWidthis back to a plain99999. The natural/minimum widths count icon + title only, since our app name and separator are hidden. Also frome473c928:syncPopoutState()usesBasePill.positionPopout(), and a click inside the bar overflow popup activates the window instead of opening the details popout. - CustomMedia —
49d7d6b2bar lyric line + track tooltip,2a8368a8cover art,a8146d2cflex sizing, all adapted. Lyrics and cover art arePluginServicetoggles (showLyrics,showCoverArt, default off), not themediaShowLyrics/mediaShowCoverArtwidget options. The text slot's natural width is ourtextWidth(or the title's width in unlimited mode), and it keeps that width while a lyric line shows.hideIconnow hides only the icon/visualizer slot, in both orientations; it used to apply to the horizontal layout only. - CustomNetworkMonitor —
66e37ff8hide when idle + compact mode, adapted. Both arePluginServicetoggles (hideWhenIdle,compactMode) with a new settings page. Compact uses our ownformatNetworkSpeedCompact(␣␣0K,␣<1K,␣12K,120M, figure-space padded), becauseCommon/Format.jscan't be imported from here. The icons takeTheme.widgetIconColor.NumericTextis not used; our tabular-numberStyledTextstays. - CustomRunningApps —
windowModelKeycollapses toCompositorService.toplevelKey; the group badge count usesTheme.onPrimaryon itsTheme.primarydot; translator context on the scratchpad menu strings. - CustomSystemTrayBar —
e473c928:_openOverflowAtgoes throughpositionPopout(), and a tray menu opened from inside the bar overflow popup is positioned by the overflow surface.OverflowLayout.expanderIcon()is not taken — its relative import can't resolve from the plugins directory, and our inline switch returns the same icons. - CustomWorkspaceSwitcher — the data layer moved onto upstream's
WorkspaceSwitcherModel(qs.Widgets), fed through ouropt()sooptionDefaultsstill applies (-165 lines). Plus585827e7/5972dd18dots style, compact indicators and roundness, adapted:indicatorStylegainsdots, andindicatorCompact,indicatorRoundnessCustomandindicatorRoundnessare newPluginServicekeys. Only the cross-axis ratios apply, since the main-axis sizing is ours, so compact affects lines and dots but not pills or cards. Dots without app icons are true circles with a fitted label.flatOuterEdgestill wins over any roundness.filledInkis not ported; our per-state text colours cover it.
Drive-by fix: CustomMedia's horizontal implicitWidth has been 0 for every fixed text
size (mediaSize 1–2) since the 2026-05-02 rebuild (25f073f), which dropped the horizontal
branch of currentContentWidth. It now reads mediaRow.implicitWidth in all horizontal modes.
115 upstream commits; four touch forked widgets, all four ported.
- CustomSystemTrayBar —
60342b74"fix: stabilize tray item key to prevent position jumps on property changes (#3386)". This one was already biting: the running build carries the matchingSessionDatav7 migration, which rewrote the savedtrayItemOrderfromid::tooltipTitleto plain ids, while the plugin still generatedid::tooltipTitlekeys — so any item whose tooltip title differs from its id fell out of its saved position. Ported as-is:getTrayItemKey()now pins a per-instance key (id, orid::nfor same-id siblings, reusing free slots) in_trayKeyByItem, cleared by aSystemTray.onItemUnregisteredConnections;resolveOrderIndex()andisTrayIdHidden()wrap the lookups. - CustomMedia —
4c65efc4"bar/media: fix icon scaling" + theiconFilled: falseline of4eb16353: the play button fills itsPlayButtonslot (buttonSize: parent.width,iconSize: 14 * contentScale, press/checked radius) instead of beingscaled up fromTheme.buttonHeightS, and uses the outlined glyph. - CustomMedia —
4b926be0(text part only), adapted. TheScrollingTextgets its final width up front instead ofanchors.fillon the animating container, so the title doesn't re-layout on every frame of the width animation. Upstream binds it tocontentRoot.measuredTextWidth; our equivalent isroot.textWidth > 0 ? root.textWidth : implicitTextWidth, which keeps the unlimited-width mode (textWidth === -1). - CustomWorkspaceSwitcher —
df71c654"feat(workspaces): add lines & card indicator styles", adapted. The style is aPluginServicesetting (indicatorStyle, defaultpills, selector in the plugin settings) rather than theworkspaceIndicatorStylewidget option, which plugin bar entries can't set. Taken:appIconsLoadermoved out ofvisualContentto a delegate-level sibling (mirrors its opacity and dragTranslate), the underline shifts for lines, the transparent outlined inactive frame and the occupied block for cards. Lines swap only the cross size tolineThickness; upstream's per-styleactiveRatio/compactRatiochanges don't apply, since the main-axis sizing is ours. Cards radii aremin(<flat-edge radius>, cornerRadiusXS), soflatOuterEdgestill wins; the urgent/drag outline stays at our2px rather thanTheme.outlineWidthFocused.
93 upstream commits. One of them — the 8e9cd3f4 squash — rewrote the whole bar widget
layer, so this sync is much larger than usual: BasePill became a one-line alias for the new
qs.Modules.SurfaceWidgets.BarPill, per-compositor logic moved into CompositorService +
Common/WorkspaceModel.js, tray and running-apps menus moved onto DankPopout /
DankContextMenu, and config version 18 moved 17 global settings into per-widget options.
First, a breakage fix. Config v18 migration deletes the old globals from settings.json,
so four widgets were silently reading undefined: CustomWorkspaceSwitcher lost its app icons
(showWorkspaceApps falsy, maxWorkspaceIcons undefined makes slice(0, undefined) return [])
plus drag reorder, the focused border, and a NaN appIconSize via
-6 + workspaceAppIconSizeOffset; CustomRunningApps lost compact mode; CustomMedia's scroll
handling went dead; CustomFocusedApp lost compact mode. Everything now goes through
SettingsData.widgetOption(<widgetType>, widgetData, key). The user's migrated values are
unrecoverable, and DMS defaults turn the workspace app icons off, so CustomWorkspaceSwitcher
carries an optionDefaults map (showWorkspaceApps, showWorkspaceIndex,
showWorkspacePadding all true) consulted by its opt() helper.
- CustomNetworkMonitor —
Ref-basedDgopServicerefcount (released when the widget or its window is hidden, instead of held for the shell's lifetime),contentThickness,contentColor. - CustomFocusedApp (672 → 529 lines) —
resolveSortedWindow,resolveActiveWindowPid,isWindowAlive,getNiriFocusedWindowand the per-compositor bodies ofupdateActiveWindow/hasWindowsOnCurrentWorkspaceall collapse intoCompositorService.activeWindowForScreen,windowPidandwindowOnActiveWorkspace, which also brings Aqueous support (b9365610).minTooltipYandisAutoHideBarnow come fromBasePill. - CustomMedia —
surfaceContext/contentScale,BarMetrics.mediaControlSizesizing, upstream'sPlayButton(DankIconButton, filled + checkable),StateLayerprev/next,surfaceLivegating on the visualiser and the scrolling title. The inlinesetTriggerPositioncalls stay: upstream could drop them becauseSurfaceWidgetFactorypositions the dash popout in its ownonClicked, which plugin widgets never reach. - CustomSystemTrayBar — taken wholesale from
upstream/masterand re-customised, since65fb97d2(menus →DankPopout, -837/+440) and the squash'sBarPillSurface/TrayItemIcondelegates left no common structure. Also picks up1bf35426and2bf2754a, and upstream fixes the fork never had: theDismissZoneoverflow mask, the window-localmapToItempopup placement fix for non-primary monitors,WlrLayershell.Overlay,StyledTextinstead of a rawText, and popup drag reordering. Auto-overflow / "Keep in Bar" is kept out by pinninguseAutomaticOverflow: falserather than by surgery — that emptiesautoOverflowBarItemsand makesisAutoOverflowTrayItem()always false, so every branch that would introduce it is dead while the file stays diff-friendly. - CustomRunningApps (2074 → 1862 lines) — patched rather than taken wholesale, because
upstream's 1600 → 545 rewrite drops the dynamic title width, the per-state colour system and
the full-height pills this fork exists for. Ported: the 230-line hand-rolled
PanelWindowcontext menu →DankContextMenupositioned throughBasePill.contextMenuAnchor(), witha3a9fe13's scratchpad entries;windowModelKeyper compositor (the fork keyed ungrouped windows by"address", which only Hyprland sets, so on niri every row shared an undefined key);skipSwitcherfiltering in scroll switching. - CustomWorkspaceSwitcher (2222 → 1572 lines) — data layer moved onto
CompositorService.currentWorkspaceKey/workspacesForScreen/isCurrentWorkspace/workspaceOccupied/workspaceUrgent/windowsOnWorkspace/switchToWorkspace/stepWorkspace/workspaceSecondaryAction/focusWindow. Ext-workspace data now comes fromQuickshell.WindowManagerscreen projections. BringsworkspacePaddingCount(e5d8d031), special-workspace hyprland slot keys (72326ec5), special workspaces with theinboxicon (a3a9fe13) and Aqueous (b9365610).
Not applicable: f6c53993 (selectedContainer instead of primaryContainer in
RunningApps) — our focused/unfocused colours come from the PluginService colour modes.
014c69b0 (workspace tile colour restyle) — same reason.
PluginService.pluginDataChangedonly ever carried the plugin id, but all four plugins declaredonPluginDataChanged(pluginId, key)and branched onkey. No plugin setting has ever applied without adms restart. All four handlers now match on the id and reload every value.Theme.onSecondaryhas not existed for some time; the five no-icon first-letter fallbacks across CustomFocusedApp, CustomRunningApps and CustomWorkspaceSwitcher now useTheme.widgetTextColor, like upstream.- CustomMedia's
SettingsData.audioScrollEnabledfallback was already dead at the previous base, soscrollModeresolved to"nothing"regardless. - CustomWorkspaceSwitcher's
paddingmirroredBarPill's old formula (default 12, plusremoveWidgetPadding).BarPillnow defaults to 8 and has droppedremoveWidgetPadding.
Exactly one of the 37 upstream commits touched our six widgets. No changes to CustomFocusedApp,
CustomMedia, CustomNetworkMonitor, CustomRunningApps, CustomSystemTrayBar,
Modules/Plugins/BasePill.qml or AudioVisualization.qml.
-
CustomWorkspaceSwitcher —
3d6e45f4"bar: focus workspace icons on any workspace and wire island overview loader", adapted. Both per-iconMouseAreas (rowAppMouseArea,colAppMouseArea) were gated onenabled: isActive, so an app icon on a non-active workspace could not be clicked to focus its window; worse, on the active workspace they swallowed the press that the outermouseAreaneeds. Upstream deletes both and hit-tests from the workspace delegate instead: newwindowIdAt(x, y)maps the press into the icon layout and readschildAt(...)?.windowId, andfocusWindowAt(x, y)focuses that window and returnstrue, which the delegate's left-buttononReleasedchecks before falling through to the workspace switch. Each icon delegate now exposesreadonly property var windowId: modelData.windowId.Adaptation: upstream aliases
iconsLayout: contentRow.item, which works because its icons are direct children of the row/column. Ours are not — the custom column layout nests them one level deeper insidecolIconsLayout(theColumnLayoutthat does the negative-margin overlap for the active icon), sochildAton theColumnwould return the wrapperItemand never find awindowId. Instead each layout component names its own container: theRowgetsid: rowIconsRow+iconsContainer: rowIconsRow, theColumngetsiconsContainer: colIconsLayout, andcontentRootreadsiconsLayout: contentRow.item?.iconsContainer ?? null.Our Hyprland branch keeps
Hyprland.dispatch(\focuswindow address:${winId}`)rather than upstream'sHyprlandService.focusWindow. Upstream also swapped its iconMouseAreas forHoverHandlers to preserve hover opacity; we need no replacement, because our icon opacity comes from the PluginService per-state colour system and never readcontainsMouse. Dropping the icons'cursorShape: Qt.PointingHandCursoris a no-op — the outermouseAreaalready sets it for every non-placeholder workspace. Drag-reorder is unaffected:wasDraggingstill returns early before the left-button branch. The// Custom:no-grouping,wsAppIconActive/wsAppIconNormalsizing, per-state colours and spacing preset are untouched.qmllinterror count is unchanged at 0, with two fewer each ofimport,unqualifiedandunresolved-type` warnings from the removed code. -
Not applicable —
fbffa410"clock: align the horizontal time block to whole pixels" touchesClock.qml, which we do not fork. The other 35 commits went to the launcher, Dank Island, dock, greeter, services, the Go core and translations.
Two of the 42 upstream commits touched our six widgets; both were ported. No changes to
CustomMedia, CustomNetworkMonitor, CustomSystemTrayBar, CustomWorkspaceSwitcher or
AudioVisualization.qml.
- CustomFocusedApp —
9a7177f9"fix(bar): prevent focused app widget from blanking and live-sync (#3258)". The widget used to blank on niri wheneverToplevelManager.activeToplevelwent null (workspace switch, popout grabbing focus). AddsisWindowAlive()andgetNiriFocusedWindow()helpers; when there is no active toplevel on niri it now falls back toNiriService.windows.find(w => w.is_focused)— orNiriService.lastFocusedWindowIdwhile the details popout is open — and maps that back to a toplevel throughCompositorService.sortedToplevels[].sourceToplevel, with anappId/titlematch as backup. Theif (!CompositorService.isNiri)guard ononActiveToplevelChangedis gone (niri needs the signal too),onAllWorkspacesChangedwas added, and the null-active branch now clears only when the window is dead or the screen's active workspace is genuinely empty. The popout trigger-position code was extracted intosyncPopoutState()soonActiveWindowChangedcan live-update an open popout (double-called viaQt.callLaterto catch post-layout geometry) orclose()it when the window goes away; a newConnectionsonfocusedWindowPopoutLoader.itemre-runsupdateActiveWindow()onshouldBeVisibleChanged/popoutClosed.hasWindowsOnCurrentWorkspaceuses the same helper and, with no focused window, now reports whether the screen's active workspace holds any window at all. Icon + title layout,maxNormalWidth: 99999,stripAppName/appIconSize/iconTitleSpacingare untouched. - CustomRunningApps —
5142d576"fix(bar): centre widget content on whole pixels (#3263)". Both copies of the ad-hocMath.max(26 + innerPadding * 0.6, Theme.barHeight - 4 - (8 - innerPadding))bar-thickness math replaced by upstream's newTheme.barThickness(innerPadding, dpr), which rounds to whole device pixels; the DPR comes fromCompositorService.getScreenScale(parentScreen)for the widget's owneffectiveBarThicknessand fromCompositorService.getScreenScale(contextMenuWindow.screen)inside the window context menu. Fixes half-pixel blur on fractional-scale outputs. Spacing presets, the PluginService colour system,appIconSizeand the title-width debounce cache are untouched.
Nothing to port. Twenty-nine upstream commits landed, but none touched any of the six widgets we
fork (FocusedApp, Media, NetworkMonitor, RunningApps, SystemTrayBar, WorkspaceSwitcher)
or Modules/Plugins/BasePill.qml. Upstream work went to the Dock, Dank Island, Greeter, Changelog,
services (IconThemeService, PolkitService, BluetoothService, ClipboardService), the Go core
and translations. Modules/Plugins/DesktopPluginWrapper.qml changed, but it wraps desktop-placed
plugins — ours are bar widgets plus one daemon — so it does not apply.
Five upstream commits touched our widgets. CustomNetworkMonitor had no upstream changes.
- CustomFocusedApp / CustomRunningApps / CustomSystemTrayBar —
ea0b158e"bar/island: fix click targets on satellites".MouseArea { anchors.fill: parent }replaced by an explicit rect grown byBasePill'sleftMargin/rightMargin/topMargin/bottomMargin, so clicks landing in the inter-widget gap or at the bar edge still hit the pill. Single-pillCustomFocusedAppextends on all four sides (upstream form); the multi-delegateCustomRunningApps(2MouseAreas) andCustomSystemTrayBar(3MouseAreas) extend on the cross axis only —x/widthwhen vertical,y/heightwhen horizontal — because side-by-side delegates would otherwise steal each other's clicks. - CustomSystemTrayBar —
3b527f1f"SystemTrayBar: add configurable icon spacing (#3183)". The spacing feature itself was already implemented here and is ahead of upstream: ouriconSpacingpreset comes fromPluginServiceunder the"SortedSystemTray"key, and both drag-shift call sites already usetrayItemSize + iconSpacing. Only the incidental fix was taken: the inline tray toggle spacer'svisible: width > 0 || height > 0→&&(with||a collapsed 0×N spacer stayed visible). - CustomSystemTrayBar —
5b8f083f"fix(tray): prefer tooltip title in menu (#3181)". Context-menu header now readstrayItem?.tooltipTitle || trayItem?.id || I18n.tr("Unknown")instead of the raw D-Bus id. Upstream'sisAutoOverflowTrayItem/ "Keep in Bar" branch is still absent here and was not introduced. - CustomRunningApps —
4447d77b"feat(Dank Island): add new island bar instance". Only the API change taken:SettingsData.barConfigs[0]?.position→SettingsData.getPrimaryBarConfig()?.positionfor the window-context-menutriggerBarPosition. Indexing[0]is wrong once more than one bar instance exists. - CustomWorkspaceSwitcher —
ea0b158e. Addedproperty real crossEdgeExtension: 0(bound automatically byWidgetHostonce the property exists) and used it in_topMargin/_bottomMarginfor horizontal bars. Inert on a normal bar; correct if the Island bar is ever enabled. - CustomWorkspaceSwitcher —
195ec3bf"feat(motion): spring-based physics & shell hybrid material animations". The drag-reorder shift'sBehavior on x/Behavior on y(NumberAnimation, 150 ms,OutCubic) replaced by twoSpringMotioninstances driven fromTheme.springPreset("fast", 150), retargeted inonShiftOffsetChanged. AlsoTheme.isLightColor(bgColor, 0.4)in place of the inline0.299r + 0.587g + 0.114b > 0.4ingetContrastingIconColor/getContrastingTextColor— mathematically identical (Theme.luminanceuses the same coefficients), and our extrabgOpacity < 0.1 → Theme.widgetTextColorguard is kept. - CustomMedia —
44e82c6e"island: break out more stuff into commonality helpers…". The ~118-line inline scrollingStyledText(hold/step/cava-tick/text-change animation) replaced by upstream'sScrollingTextfromqs.Widgets, which carries byte-identical logic.textContainer.widthnow readsmediaText.implicitTextWidthinstead ofmediaText.implicitWidth, because the component is anItemwrapper whose ownimplicitWidthis 0 (this is the samecontentWidth→implicitTextWidthcorrection upstream made inc580aa26). Behaviour difference: withscrollTitleEnabledoff, overflowing text now elides with…instead of hard-clipping.mediaSize/reverseOrder/hideIconand the unlimited-width path (textWidth === -1) are untouched. - CustomMedia / AudioVisualization —
44e82c6e, adapted. Our copy is still the pre-shaderRectangle+Repeaterfork, so only two things carry over:livegains&& enabled, and the fakeMath.random()cava fallbackTimeris gone (upstream deleted it;CustomMediaalready shows amusic_noteicon whenever cava is unavailable). Drive-by: theBehavior on heightguarded on!CavaService.cavaAvailablewent with it — nothing mutatesCavaService.valueswhile cava is down, so it could no longer fire. Upstream'sidleIconName/barColor/showBarsproperties are not needed, sinceCustomMediaowns its own idle icon. - CustomFocusedApp —
b7034fca"fix(bar): prevent focused app overlap (#3190)" and2c471e1c"clip title overflow horizontally only" not ported. Both clamp the pill toavailableWidth, butDankBarContent.qmlwiresavailableWidthonly for its own built-inFocusedAppinstance —WidgetHost/PluginComponentnever pass it to plugin widgets. Our property would sit at its stale default of400, capping the deliberately unlimited (maxNormalWidth: 99999) pill. - CustomMedia —
c580aa26"fix media text collapse" not applicable. The fix targets upstream'smeasuredTextWidth/adaptiveWidthEnabledpath, which our fork does not have (we size fromtextWidthdirectly).
Two upstream commits touched our six widgets; both were ported.
- CustomFocusedApp —
a669253c"feat(bar): add focused window details popout (#3139)". Left-click on the pill now opens upstream'sFocusedWindowContextMenupopout (window details + PID) via a newfocusedWindowPopoutLoader;cursorShape: Qt.PointingHandCursor; addedresolveSortedWindow()/resolveActiveWindowPid()(niri PID comes fromNiriService.windows[].pid, added to the service in the same upstream commit). Plugin importsqs.Modules.DankBar.Widgetsto reach the popout type. Icon + title layout, unlimited width and the strip-app-name setting are untouched. Requires a DMS build containinga669253c(quickshell/Modules/DankBar/Widgets/FocusedWindowContextMenu.qml+NiriServicepid): on the currentc1894a3store path the plugin fails to load untildmsis rebuilt from the fork branch. - CustomWorkspaceSwitcher —
90c5f65b"fix(workspaces/hyprland): recover workspace mapping on monitor hotplug". The hardcoded{id: 1, name: "1"}fallback for the per-monitor Hyprland filter replaced byhyprlandMonitorWorkspaces(), which falls back toHyprland.monitors[].activeWorkspace(#3133). Hyprland-only path — inert on niri, ported for diff-parity. Individual-icon (// Custom:) code,PluginServicecolours and spacing presets untouched. - No changes to CustomMedia, CustomNetworkMonitor, CustomRunningApps, CustomSystemTrayBar, or
AudioVisualization.qml.
Three upstream commits touched our six widgets; all three were ported.
- CustomSystemTrayBar —
34626070"fix(tray): don't dismiss click-opened tray menus from hover controller (#2979)". Real bug fix here, not just diff-parity: the running build'sTrayMenuManager.closeHoverMenus()skips any menu whoseopenedByHover !== true, and our fork had no such property, soDankBarHoverControllercould never dismiss a hover-opened tray menu. AddedopenedByHoverto the tray-menu component and threaded abyHoverargument through bothshowForTrayItem()overloads;openHoverAtGlobalPoint()passestrue, every click path leaves itundefined. - CustomSystemTrayBar —
2baf0482"feat(tray): add IPC action to open tray menus (#2701)". Added theConnections { target: TrayMenuManager }block withonOpenTrayMenuRequested.claimMenuRequest()/findTrayItem()both exist in the running build. Independent of our drag-and-drop reordering and ofSessionData.trayItemOrder; upstream's auto-overflow / "Keep in Bar" is still absent here, as intended. - CustomRunningApps —
b0b9b615"dock/apps: support minimize". Ported in full across both delegate branches:isMinimizedreadonly property +opacity: 0.4on the icon, Steam fallback icon and letter fallback..activate()→CompositorService.activateToplevel()(scroll switching, group cycling) andCompositorService.toggleToplevel()(single-window click).- Window context menu rewritten from a fixed 100×32 close-only rect into a 120-wide
Columnwith a conditional Minimize/Restore row plus Close. - Context-menu
yPosfor bottom bars stopped using the hardcoded32and now readswindowContextMenuLoader.item.menuHeight. This is the part that actually changes behaviour on this machine — minimize itself is inert on niri, sinceDankCommon/Common/Compositor.qmlreturnssupportsMinimize: falsewheneverNIRI_SOCKETis set, which makescanMinimize()false and hides the Minimize row. - Our customizations were untouched:
appIconSize, thePluginServicecolour modes viaroot.getTextColor(isFocused), the debug overlays, the root-level title debounce cache.
- No changes to CustomFocusedApp, CustomMedia, CustomNetworkMonitor, CustomWorkspaceSwitcher, or
AudioVisualization.qml.
Only one upstream commit touched our six widgets: c67b1850 "qs: large sweep of dead code removals".
- CustomWorkspaceSwitcher —
c67b1850: localescapeSwayWorkspaceName()/dispatchSwayWorkspace()(15 lines) deleted in favour ofCompositorService.dispatchSwayWorkspace(); 3 call sites redirected. Body is byte-identical to the service's, which is present in the running build (CompositorService.qml:1094). Sway/scroll/miracle paths only — inert on niri, taken for diff-parity.I3import still needed elsewhere in the file. - CustomNetworkMonitor —
c67b1850not applicable. Upstream swappedformatNetworkSpeed()forFormat.formatRate(), but (a) our formatter is the customization (figure spaces\u2007,"0 KB/s"/"<1 KB/s", 3-char right-aligned rounding), and (b) the newimport "../../../Common/Format.js"is relative toquickshell/Modules/DankBar/Widgets/and cannot resolve from the plugins directory. - Plugins setting components —
c67b1850also refactoredquickshell/Modules/Plugins/*Setting.qml(findSettings()→QmlUtils.findSettings()). No public API change, and all 11 local slider forks (SteppedSliderSetting/RealSliderSetting/LabeledSliderSetting) carry their ownfindSettings()— unaffected. Same relative-import blocker applies, so not ported. - No changes to CustomFocusedApp, CustomMedia, CustomRunningApps, CustomSystemTrayBar.
- CustomMedia —
fe1a783e/dc924618stable-metadata centralisation: the local_stableTitle/_stableArtist/_syncMeta()block (26 lines) deleted in favour ofMprisController.stableTitle/.stableArtist; all fouractivePlayer.next()call sites routed throughMprisController.next(). OurcanGoNext/canGoPreviousscroll guards are unaffected.2986e354/cdaedad9/c3fa7b2etitle-scroll rewrite (#2863): the infiniteSequentialAnimationreplaced byTimer-drivenstepScroll()that ridesCavaService.valuesChangedticks when the visualizer is live. A runningNumberAnimationcommits a frame every vsync, so two unsynchronized tick sources nearly doubled the surface commit rate. Also gated on window visibility +_isPlaying, andxrounded to whole pixels.- AudioVisualization — adapted, not ported. Our copy predates upstream's shader rewrite (
bandsA/bandsBvectors), so only the two ideas carry over: alivegate (visible && Window.window?.visible && isPlaying) on theRefloader, fallback timer andCavaServiceconnection; and levels quantized to 1/32 with identical frames dropped before assigningbarHeights.
- CustomSystemTrayBar —
fb2dbcedmenu overflow:rawHeightcapped atmaskHeight - 20andmenuColumnwrapped in aDankFlickable(interactive: contentHeight > height), so long tray menus scroll instead of running off-screen. OurTheme.primaryHoverhover colours andSessionData.isHiddenTrayIdvisibility logic preserved; upstream's auto-overflow / "Keep in Bar" branch is absent from our copy and was not introduced.1402d231vbar menu positioning: vertical-orientation branches clamp against windowwidth/heightinstead ofmaskX/maskYbounds (4 blocks — overflow menu x/y, tray menu x/y).- Icon-size token sweep: hardcoded
16/14/10→Theme.iconSizeSmalland derivations; checkboxradius: … ? 8 : 2→width / 2.
- CustomWorkspaceSwitcher —
4fb69957Hyprland re-focus fix (#2830):ConnectionsonHyprlandrawEventcallingupdateAllData()foractivewindow/activewindowv2. Inert on niri (enabled: CompositorService.isHyprland) — taken for diff-parity with upstream.296b3a3d(mango dispatch socket) not applicable — our copy has nommsg/overview-toggle button.
- CustomMedia —
52ed7194fix: missinganchors.verticalCenteron themediaInfoRow. - CustomNetworkMonitor —
093acdbfspacing-token sweep: hardcoded2/4→Theme.spacingXXS/Theme.spacingXS. - CustomRunningApps — close-button simplification:
BlurService.borderWidth/.borderColorused directly (enabled-check moved inside the service);"transparent"→Theme.withAlpha(..., 0)for smoother hover animation. - CustomSystemTrayBar —
- Cosmetic sweep:
Theme.outlineHeavyreplacing hardcodedQt.rgba(Theme.outline, 0.2),Theme.spacingXXS,closeWithAction()indirection removed, hover"transparent"→Theme.withAlpha(Theme.primaryHover, 0). fefc0afamenu boundary clamping: overflow/context menus now clamp againstmaskX/Y/Width/Heightinstead of raw screen bounds, plusRegionmasks so menu content areas are included in the popup hit region.HyprlandFocusGrab→DankFocusGrabmigration (fixes Hyprland focus-stealing bug #2577), wired through theKeyboardFocussingleton (KeyboardFocus.keyboardFocus()/.wantsGrab()/.barWindows) — this also closes a pre-existing gap where the file had hand-rolled focus logic bypassing that singleton entirely.6bee1b2cHoverMode hook functions added (hoverTriggerAtGlobalPoint,openHoverAtGlobalPoint) — purely additive.
- Cosmetic sweep:
- CustomWorkspaceSwitcher —
effectiveScreenNamesimplified toBarWidgetService.getFocusedScreenName(), replacing the manual per-compositor switch (incidentally fixes focus-follow for Mango, which the old switch never had a case for).- Sway/Hyprland named-workspace correctness: stripped
<num>:<name>prefix, stable ordering (numbered first, named after by name), safe dispatch for named workspaces (workspace "name"/workspace name:x), Hyprland special-workspace filtering. - Stable placeholder/slot pooling (
_placeholderPool,_hyprSlotPool) so ScriptModel reuses delegate identity across workspace churn instead of recreating pills (reduces animation jitter);onCompositorChangedresets both pools. DankColorAnimationswap for the pill's background-color transition (premultiplied-alpha tween instead of plainColorAnimation), applied on top of our own PluginService-based color system —SettingsData.workspace*ColorModeis unrelated to us and was not touched.
- Per-monitor "unfocused monitor appearance" color refactor (
effectiveColorMode/effectiveCustomColor/workspaceUnfocusedMonitor*) — CustomWorkspaceSwitcher has its own PluginService-based color system;SettingsData.workspace*ColorModeisn't used here at all. - CustomFocusedApp's new
showIconsetting + horizontal-icon width recalculation — superseded by our existing "icon + title, unlimited width" customization, which predates and already covers this. - CustomMedia's prev/next hover-color fix — doesn't apply; we already use a different color scheme (
Theme.primaryHover) there.
- CustomWorkspaceSwitcher: dwl/mango dead code — fixed in the 2026-09-19 sync. The
CompositorService.isDwl/DwlServicebranches are gone; mango now goes throughCompositorService's workspace API like every other compositor. - CustomWorkspaceSwitcher: named sway workspaces + app icons — fixed in the 2026-09-19
sync.
getWorkspaceIconsno longer keys off.num; it asksCompositorService.windowsOnWorkspace(record).
Shows icon + window title instead of app name + separator + title.
Changes:
- App name text hidden (
visible: false) - Separator dot hidden
- Icon displayed alongside title only
- Unlimited width; the bar's flex resolver shrinks the pill when sections would collide
- Left-click opens upstream's
FocusedWindowContextMenupopout (window details + PID), as in upstream sincea669253c - Settings panel with:
- "Strip App Name from Title" - Smart removal of app name, version numbers, instance markers, and brand words from titles
Configurable media widget with settings panel.
Changes:
- Configurable text width (none/small/medium/unlimited)
- Reverse layout order option
- Hide icon option (hides the icon/visualizer slot)
- Lyric line (with a track tooltip on hover) and cover art toggles, both off by default
- Mouse wheel volume control (when player supports it)
- Mouse wheel: 5% volume increments
- Touchpad: smooth 1% increments with accumulator
- Per-widget or global settings via
PluginService.loadPluginData
Network speed monitor with fixed-width formatting.
Changes:
- Uses figure spaces (
\u2007) for consistent width alignment - Shows
0 KB/sor<1 KB/sfor low values - Color-coded: download (Theme.info/blue), upload (Theme.error/red)
- Padded numbers to 3 digits for stable layout
- Settings panel: "Hide When Idle" (below 1 KB/s both ways) and "Compact Mode" (
12Kinstead of12 KB/s)
Enhanced running apps taskbar.
Changes:
- Scroll wheel switches between windows
- Middle-click closes window
- Right-click context menu (
DContextMenu) with Minimize/Restore, scratchpad moves and Close - Grouped windows show badge with count
- Click cycles through grouped windows
- Hover tooltips active via
tooltipLoader(an earlier revision had them commented out) - Smart dynamic title width - Non-linear algorithm shrinks longer titles proportionally more when space is constrained
- Niri column indicators - Visual frame around windows in the same Niri column
- Detects stacked vs tabbed columns (different colors)
- Frame opacity increases when group has focused window
- Niri floating indicator - Line indicator on floating windows
- Custom background colors - Separate settings for focused and unfocused pills
- Color selection from Theme palette (Primary, Secondary, Surface variants, etc.)
- Individual opacity sliders (0-100%) for each state
- Custom text colors - Auto-contrast or manual selection for focused/unfocused states
- Flat outer edge - Option to remove rounded corners on the edge facing screen border
- Settings panel with:
- "Strip App Name from Title" - Removes app name, version numbers, instance markers from window titles
- "Title Compression" - Configurable ratio for how aggressively longer titles shrink (1=equal, 2=normal, up to 10=extreme)
System tray with custom icon sizes, spacing, and hover colors. Uses upstream drag-and-drop reordering.
Changes:
- Custom icon size: Configurable via
PluginService.loadPluginData("SortedSystemTray", "iconSize", 18) - Custom icon spacing: Configurable via
PluginService.loadPluginData("SortedSystemTray", "iconSpacing", "M")(presets: 0, XS, S, M, L, XL) - Custom hover color:
Theme.primaryHoveron the four bar-visible hover surfaces (both carets, the main delegate, the inline expanded delegate) instead of upstream'sTheme.onSurfacestate-layer alpha. Menu chrome keeps upstream's styling. - Item size is the icon:
trayItemSizeisconfiguredIconSizewith no padding; upstream pads it byspacingXS + spacingXXS, which would double up with the spacing preset. - Drag-and-drop reordering: Uses upstream's drag-and-drop with spacing-aware slot calculations
- No auto-overflow:
useAutomaticOverflowis pinnedfalse, which emptiesautoOverflowBarItemsand makesisAutoOverflowTrayItem()always false — every upstream branch that would add auto-overflow or "Keep in Bar" is dead, with no surgery on the file.
Enhanced workspace indicator with app icons.
Changes:
- Shows individual app icons per workspace (no grouping)
- Patched:
const key = w.aqueousKey || \${moddedId}_${i}`(unique key per window), andstableIconCountreturnsCompositorService.windowsOnWorkspace(record).length` rather than upstream's per-app grouped count
- Patched:
- Active window icons enlarged (36px vs 24px)
- Click on app icon focuses that window, via
windowIdAt/focusWindowAthit-testing andCompositorService.focusWindow - Workspace index always shown alongside icons
- Steam games show gamepad icon, Quickshell shows themed icon
-
Custom background colors - Settings for all 4 states (active, unfocused, occupied, urgent)
- Color selection from Theme palette
- Individual opacity sliders (0-100%) for each state
- Custom text/icon colors - Auto-contrast or manual selection for each state
- Flat outer edge - Option to remove rounded corners on the screen-edge side
-
Indicator style -
indicatorStyle(pills/lines/cards/dots, defaultpills) viaPluginService, in place of upstream'sworkspaceIndicatorStylewidget option; plusindicatorCompactand a custom roundness (indicatorRoundnessCustom+indicatorRoundness0–100)
OLED burn-in protection daemon using DDC/I2C hardware brightness control.
Changes:
- Multi-stage dimming: active → dimming → holding → blacking → black → DPMS
- DDC hardware brightness: Uses I2C/DDC (independent of gamma/night light)
- Smooth fades: Ease-out cubic transitions at ~2.5 FPS (within DDC limits)
- Auto-detect DDC device: Falls back gracefully if no DDC device found
- Configurable stages: All timeouts, brightness levels, and fade durations adjustable
- Settings panel with:
- Enable/disable toggle
- Dim timeout (1-10 min)
- Dim brightness level (5-80%)
- Hold duration at dim (1-10 min)
- DPMS delay after black (1-30 min)
- Fade speed (3-20 sec)
Third-party plugin for Docker/Podman container management. See dockerManager/README.md.