Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions config/Config.qml
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,8 @@ Singleton {
property string noMediaDisplay: "userHost"
property string customText: "Ambxst"
property bool disableHoverExpansion: true
property bool showCavaOnMedia: false
property int cavaBars: 16
}
}

Expand Down Expand Up @@ -1099,7 +1101,7 @@ Singleton {
if (current.ambxst.dashboard && typeof current.ambxst.dashboard === "object" && !current.ambxst.dashboard.modifiers) {
console.log("Migrating nested ambxst binds to flat structure...");
const nested = current.ambxst.dashboard;

// Map old names to new names and update arguments
if (nested.widgets) {
current.ambxst.launcher = nested.widgets;
Expand Down Expand Up @@ -3037,7 +3039,7 @@ Singleton {
// Trigger save
GlobalStates.markShellChanged();
}
}
}
// If notch moves top
else if (notchPosition === "top") {
// Restore Dock if displaced
Expand Down Expand Up @@ -3139,20 +3141,20 @@ Singleton {

function resolveColor(colorValue) {
if (!colorValue) return "transparent"; // Fallback

if (isHexColor(colorValue)) {
return colorValue;
}

// Check Colors singleton
if (typeof Colors === 'undefined' || !Colors) return "transparent";
return Colors[colorValue] || "transparent";

return Colors[colorValue] || "transparent";
}

function resolveColorWithOpacity(colorValue, opacity) {
if (!colorValue) return Qt.rgba(0,0,0,0);

const color = isHexColor(colorValue) ? Qt.color(colorValue) : (Colors[colorValue] || Qt.color("transparent"));
return Qt.rgba(color.r, color.g, color.b, opacity);
}
Expand Down
4 changes: 3 additions & 1 deletion config/defaults/notch.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ var data = {
"keepHidden": false,
"noMediaDisplay": "userHost",
"customText": "Ambxst",
"disableHoverExpansion": true
"disableHoverExpansion": true,
"showCavaOnMedia": false,
"cavaBars": 16
}
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ install_dependencies() {

local PKGS=(
kitty tmux fuzzel network-manager-applet blueman
pipewire wireplumber pavucontrol easyeffects ffmpeg x264 playerctl
pipewire wireplumber pavucontrol easyeffects cava ffmpeg x264 playerctl
qt6-base qt6-declarative qt6-wayland qt6-svg qt6-tools qt6-imageformats qt6-multimedia qt6-shadertools
libwebp libavif syntax-highlighting breeze-icons hicolor-icon-theme
brightnessctl ddcutil fontconfig grim slurp imagemagick jq sqlite upower
Expand Down
25 changes: 25 additions & 0 deletions modules/widgets/dashboard/controls/ShellPanel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,31 @@ Item {
}
}
}

ToggleRow {
label: "Cava Visualizer on Media"
checked: Config.notch.showCavaOnMedia ?? false
onToggled: value => {
if (value !== Config.notch.showCavaOnMedia) {
GlobalStates.markShellChanged();
Config.notch.showCavaOnMedia = value;
}
}
}

NumberInputRow {
label: "Cava Bars"
visible: Config.notch.showCavaOnMedia ?? false
value: Config.notch.cavaBars ?? 16
minValue: 4
maxValue: 32
onValueEdited: newValue => {
if (newValue !== Config.notch.cavaBars) {
GlobalStates.markShellChanged();
Config.notch.cavaBars = newValue;
}
}
}
}

Separator {
Expand Down
124 changes: 114 additions & 10 deletions modules/widgets/defaultview/CompactPlayer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -124,24 +124,62 @@ Item {
}
}

// Cava visualizer support
readonly property bool cavaEnabled: (Config.notch.showCavaOnMedia ?? false) && isPlaying && player !== null
property var cavaBars: []
property bool cavaRestartHold: false

Timer {
id: cavaRestartTimer
interval: 40
repeat: false
onTriggered: compactPlayer.cavaRestartHold = false
}

Connections {
target: Config.notch
function onCavaBarsChanged() {
if (!compactPlayer.cavaEnabled || !compactPlayer.visible)
return;
compactPlayer.cavaRestartHold = true;
cavaRestartTimer.restart();
}
}

Process {
id: cavaProcess
running: compactPlayer.cavaEnabled && compactPlayer.visible && !compactPlayer.cavaRestartHold
command: [
"bash", "-c",
"printf '[general]\\nbars=" + (Config.notch.cavaBars ?? 16) + "\\n[output]\\nmethod=raw\\nraw_target=/dev/stdout\\ndata_format=ascii\\nascii_max_range=7\\nbar_delimiter=59\\nframe_delimiter=10\\n' > /tmp/ambxst-cava.ini && exec cava -p /tmp/ambxst-cava.ini"
]

stdout: SplitParser {
splitMarker: "\n"
onRead: data => {
const trimmed = data.trim();
if (trimmed === "") return;
const parts = trimmed.split(";");
const parsed = parts.map(x => parseInt(x, 10) || 0);
if (parsed.length > 0) {
compactPlayer.cavaBars = parsed;
}
}
}
}

StyledRect {
variant: "common"
anchors.fill: parent
radius: Styling.radius(-4)

Text {
id: mediaTitle
Item {
id: mediaTitleContainer
anchors.centerIn: parent
width: parent.width - 32
text: compactPlayer.displayedTitle
font.family: Config.theme.font
font.pixelSize: Styling.fontSize(0)
font.bold: true
color: Colors.overBackground
elide: Text.ElideRight
width: parent.width
height: parent.height
visible: opacity > 0
opacity: (compactPlayer.notchHovered && compactPlayer.player) ? 0.0 : 1.0
horizontalAlignment: Text.AlignHCenter
z: 5

Behavior on opacity {
Expand All @@ -151,6 +189,72 @@ Item {
easing.type: Easing.OutQuart
}
}

Text {
id: mediaTitle
anchors.centerIn: parent
width: parent.width - 32
text: compactPlayer.displayedTitle
font.family: Config.theme.font
font.pixelSize: Styling.fontSize(0)
font.bold: true
color: Colors.overBackground
elide: Text.ElideRight
visible: opacity > 0
opacity: compactPlayer.cavaEnabled ? 0.0 : 1.0
horizontalAlignment: Text.AlignHCenter

Behavior on opacity {
enabled: Config.animDuration > 0
NumberAnimation {
duration: Config.animDuration
easing.type: Easing.OutQuart
}
}
}

// Cava bars visualizer (shown in place of title when enabled + playing)
Row {
id: cavaBarsRow
anchors.fill: parent
anchors.margins: 8
spacing: Math.max(1, (parent.width - anchors.margins * 2 - compactPlayer.cavaBars.length * barWidth) / Math.max(1, compactPlayer.cavaBars.length - 1))
visible: opacity > 0
opacity: compactPlayer.cavaEnabled ? 1.0 : 0.0

Behavior on opacity {
enabled: Config.animDuration > 0
NumberAnimation {
duration: Config.animDuration
easing.type: Easing.OutQuart
}
}

readonly property real barWidth: Math.max(2, Math.floor((parent.width - anchors.margins * 2) / Math.max(1, compactPlayer.cavaBars.length) - 1))

Repeater {
model: compactPlayer.cavaBars.length
Item {
required property int index
width: cavaBarsRow.barWidth
height: cavaBarsRow.height
Rectangle {
width: parent.width
height: Math.max(2, (compactPlayer.cavaBars[index] / 7.0) * cavaBarsRow.height)
anchors.bottom: parent.bottom
color: Colors.overBackground
radius: 1
Behavior on height {
enabled: Config.animDuration > 0
NumberAnimation {
duration: 50
easing.type: Easing.OutQuart
}
}
}
}
}
}
}

ClippingRectangle {
Expand Down
1 change: 1 addition & 0 deletions nix/packages/media.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{ pkgs }:

with pkgs; [
cava
gpu-screen-recorder
mpvpaper

Expand Down