From ce56346d149cf1e5508497255449d482f3dbb964 Mon Sep 17 00:00:00 2001 From: brendancohan Date: Mon, 18 May 2026 21:54:18 -0400 Subject: [PATCH 1/2] feat(music): add smart window focus to music popup source link --- .../scripts/quickshell/music/MusicPopup.qml | 97 +++++++++++++++---- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/.config/hypr/scripts/quickshell/music/MusicPopup.qml b/.config/hypr/scripts/quickshell/music/MusicPopup.qml index f59d7bb..50f77b6 100644 --- a/.config/hypr/scripts/quickshell/music/MusicPopup.qml +++ b/.config/hypr/scripts/quickshell/music/MusicPopup.qml @@ -15,7 +15,7 @@ Item { Scaler { id: scaler // Uses the physical screen width so the popup scales synchronously - currentWidth: Screen.width + currentWidth: masterWindow.width } // Helper function scoped to the root Item for easy access @@ -218,15 +218,61 @@ Item { // --- UTILITIES & OPTIMISTIC UPDATES --- function execCmd(cmdStr) { - var safeCmd = cmdStr.replace(/`/g, "\\`"); - var p = Qt.createQmlObject(` - import Quickshell.Io - Process { - command: ["bash", "-c", \`${safeCmd}\`] - running: true - onExited: (exitCode) => destroy() - } - `, root); + Quickshell.execDetached(["bash", "-c", cmdStr]); + } + + function focusPlayer() { + if (!root.musicData || !root.musicData.playerName) return; + + let player = root.musicData.playerName.toLowerCase(); + let title = root.musicData.title || ""; + // Escape single quotes for the shell command + let escapedTitle = title.replace(/'/g, "'\\''"); + + // Close the popup first to ensure focus can transition cleanly + if (typeof masterWindow !== "undefined") { + masterWindow.switchWidget("hidden", ""); + } + + let shellCmd = ` + PLAYER="${player}" + TITLE='${escapedTitle}' + + # Fetch all clients once + CLIENTS=$(hyprctl clients -j) + + # 1. Try to match Player AND Title (Mutual contains check for instance handling) + CLIENT_INFO=$(echo "$CLIENTS" | jq -c --arg p "$PLAYER" --arg t "$TITLE" ' + .[] | select( + ( + (.class | ascii_downcase | (contains($p) or ($p | contains(.)))) or + (.initialClass | ascii_downcase | (contains($p) or ($p | contains(.)))) + ) + and (.title | ascii_downcase | contains($t | ascii_downcase)) + )' | head -n 1) + + # 2. Browser & Common Player Fallback: Search by title in likely candidates + if [ -z "$CLIENT_INFO" ]; then + CLIENT_INFO=$(echo "$CLIENTS" | jq -c --arg t "$TITLE" ' + .[] | select( + (.class | ascii_downcase | (contains("firefox") or contains("chrome") or contains("chromium") or contains("brave") or contains("vlc") or contains("mpv") or contains("audacious"))) + and (.title | ascii_downcase | contains($t | ascii_downcase)) + )' | head -n 1) + fi + + # 3. Last Resort: Match by Player name only + if [ -z "$CLIENT_INFO" ]; then + CLIENT_INFO=$(echo "$CLIENTS" | jq -c --arg p "$PLAYER" ' + .[] | select((.class | ascii_downcase | contains($p)) or ($p | contains(.class | ascii_downcase))) + ' | head -n 1) + fi + + if [ -n "$CLIENT_INFO" ]; then + ADDR=$(echo "$CLIENT_INFO" | jq -r ".address") + hyprctl dispatch focuswindow address:"$ADDR" + fi + `; + execCmd(shellCmd); } function applyPresetOptimistically(presetName) { @@ -284,7 +330,7 @@ Item { Process { id: musicProc running: true - command: ["bash", "-c", "$HOME/.config/hypr/scripts/quickshell/music/music_info.sh"] + command: ["bash", "-c", "exec $HOME/.config/hypr/scripts/quickshell/music/music_info.sh"] stdout: StdioCollector { onStreamFinished: { if (this.text) { @@ -306,7 +352,7 @@ Item { Process { id: eqProc running: true - command: ["bash", "-c", "$HOME/.config/hypr/scripts/quickshell/music/equalizer.sh get"] + command: ["bash", "-c", "exec $HOME/.config/hypr/scripts/quickshell/music/equalizer.sh get"] stdout: StdioCollector { onStreamFinished: { if (this.text) { @@ -727,15 +773,24 @@ Item { Text { text: root.musicData.deviceName || "Speaker"; color: root.overlay2; font.family: "JetBrains Mono"; font.pixelSize: root.s(12); font.bold: true } } } - Text { - text: "VIA " + (root.musicData.source || "Offline") - color: root.overlay2 // Better matugen match - font.family: "JetBrains Mono" - font.pixelSize: root.s(12) - font.bold: true - font.italic: true - } - } + MouseArea { + Layout.fillWidth: true + Layout.preferredHeight: root.s(20) + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onClicked: root.focusPlayer() + + Text { + anchors.fill: parent + text: "VIA " + (root.musicData.source || "Offline") + color: parent.containsMouse ? root.mauve : root.overlay2 + font.family: "JetBrains Mono" + font.pixelSize: root.s(12) + font.bold: true + font.italic: true + Behavior on color { ColorAnimation { duration: 150 } } + } + } } } // PROGRESS AREA CHUNK From ac2cc0d0f58bcf92685470f0c35b0a8123b3b0cf Mon Sep 17 00:00:00 2001 From: brendancohan Date: Mon, 17 Aug 2026 22:53:26 -0400 Subject: [PATCH 2/2] feat: improve topbar toggle logic and movies widget streams - Converted topbar auto-hide into a hard manual toggle that natively slides the bar fully off-screen - Replaced public embedding streams in movies widget with native Stremio deep-link execution --- .config/hypr/default_settings.json | 3 +- .config/hypr/scripts/qs_manager.sh | 12 ++ .config/hypr/scripts/quickshell/TopBar.qml | 110 +++++++++++++----- .../scripts/quickshell/movies/MovieWidget.qml | 20 ++-- 4 files changed, 103 insertions(+), 42 deletions(-) diff --git a/.config/hypr/default_settings.json b/.config/hypr/default_settings.json index 4b6d27f..805ab54 100644 --- a/.config/hypr/default_settings.json +++ b/.config/hypr/default_settings.json @@ -2,6 +2,7 @@ "uiScale": 1.0, "openGuideAtStartup": true, "topbarHelpIcon": true, + "topbarHide": false, "workspaceCount": 8, "wallpaperDir": "", "language": "us", @@ -27,7 +28,7 @@ {"type":"bind","mods":"$mainMod","key":"E","dispatcher":"exec","command":"nautilus"}, {"type":"bind","mods":"$mainMod","key":"R","dispatcher":"exec","command":"bash ~/.config/hypr/scripts/reload.sh"}, {"type":"bind","mods":"$mainMod","key":"C","dispatcher":"exec","command":"~/.config/hypr/scripts/qs_manager.sh toggle clipboard"}, - {"type":"bind","mods":"$mainMod","key":"P","dispatcher":"exec","command":"~/.config/hypr/scripts/qs_manager.sh toggle movies"}, + {"type":"bind","mods":"$mainMod","key":"M","dispatcher":"exec","command":"bash ~/.config/hypr/scripts/qs_manager.sh toggle movies"}, {"type":"bind","mods":"$mainMod","key":"D","dispatcher":"exec","command":"bash ~/.config/hypr/scripts/qs_manager.sh toggle applauncher"}, {"type":"bind","mods":"$mainMod SHIFT_L","key":"S","dispatcher":"exec","command":"bash ~/.config/hypr/scripts/qs_manager.sh toggle settings"}, {"type":"bind","mods":"$mainMod","key":"Q","dispatcher":"exec","command":"bash ~/.config/hypr/scripts/qs_manager.sh toggle music"}, diff --git a/.config/hypr/scripts/qs_manager.sh b/.config/hypr/scripts/qs_manager.sh index 6e09f3b..6a41bdb 100755 --- a/.config/hypr/scripts/qs_manager.sh +++ b/.config/hypr/scripts/qs_manager.sh @@ -173,6 +173,18 @@ if [[ "$ACTION" == "open" || "$ACTION" == "toggle" ]]; then exit 0 fi + if [[ "$TARGET" == "topbar_hide" ]]; then + SETTINGS_FILE="$HOME/.config/hypr/settings.json" + if [ -f "$SETTINGS_FILE" ]; then + CURRENT_VAL=$(jq -r '.topbarHide // false' "$SETTINGS_FILE") + NEW_VAL="true" + [[ "$CURRENT_VAL" == "true" ]] && NEW_VAL="false" + TEMP_FILE=$(mktemp) + jq ".topbarHide = $NEW_VAL" "$SETTINGS_FILE" > "$TEMP_FILE" && mv "$TEMP_FILE" "$SETTINGS_FILE" + fi + exit 0 + fi + if [[ "$TARGET" == "wallpaper" ]]; then handle_wallpaper_prep CURRENT_SRC="" diff --git a/.config/hypr/scripts/quickshell/TopBar.qml b/.config/hypr/scripts/quickshell/TopBar.qml index 7c1c871..1afd22f 100644 --- a/.config/hypr/scripts/quickshell/TopBar.qml +++ b/.config/hypr/scripts/quickshell/TopBar.qml @@ -62,9 +62,18 @@ Variants { property int barHeight: s(48) - height: barHeight - margins { top: s(8); bottom: 0; left: s(4); right: s(4) } - exclusiveZone: barHeight + property bool isBarHidden: false + + implicitHeight: barHeight + margins { + top: isBarHidden ? (-barHeight - s(8)) : s(8) + bottom: 0 + left: s(4) + right: s(4) + } + Behavior on margins.top { NumberAnimation { duration: 400; easing.type: Easing.OutCubic } } + + exclusiveZone: isBarHidden ? 0 : barHeight color: "transparent" MatugenColors { @@ -81,6 +90,7 @@ Variants { property int workspaceCount: 8 property string activeWidget: "" + property string mouseBatPercent: "" property bool isSettingsOpen: activeWidget === "settings" property real settingsSlideProgress: isSettingsOpen ? 1.0 : 0.0 @@ -110,7 +120,7 @@ Variants { Process { id: widgetWatcher - command: ["bash", "-c", "while [ ! -f " + paths.runDir + "/current_widget ]; do sleep 1; done; inotifywait -qq -e modify,close_write " + paths.runDir + "/current_widget"] + command: ["bash", "-c", "while [ ! -f " + paths.runDir + "/current_widget ]; do sleep 1; done; exec inotifywait -qq -e modify,close_write " + paths.runDir + "/current_widget"] running: true onExited: { widgetPoller.running = false; @@ -133,7 +143,7 @@ Variants { Process { id: recWatcher running: true - command: ["bash", "-c", "inotifywait -qq -e create,delete,modify,close_write " + paths.getCacheDir("recording") + "/ 2>/dev/null || sleep 2"] + command: ["bash", "-c", "exec inotifywait -qq -e create,delete,modify,close_write " + paths.getCacheDir("recording") + "/ 2>/dev/null || sleep 2"] onExited: { recPoller.running = false; recPoller.running = true; @@ -155,7 +165,7 @@ Variants { Process { id: updateWatcher running: true - command: ["bash", "-c", "inotifywait -qq -e create,delete,close_write " + paths.getCacheDir("updater") + "/ 2>/dev/null || sleep 5"] + command: ["bash", "-c", "exec inotifywait -qq -e create,delete,close_write " + paths.getCacheDir("updater") + "/ 2>/dev/null || sleep 5"] onExited: { updatePoller.running = false; updatePoller.running = true; @@ -178,6 +188,10 @@ Variants { barWindow.showHelpIcon = parsed.topbarHelpIcon; } + if (parsed.topbarHide !== undefined && barWindow.isBarHidden !== parsed.topbarHide) { + barWindow.isBarHidden = parsed.topbarHide; + } + if (parsed.workspaceCount !== undefined && barWindow.workspaceCount !== parsed.workspaceCount) { barWindow.workspaceCount = parsed.workspaceCount; wsDaemon.running = false; @@ -191,7 +205,7 @@ Variants { Process { id: settingsWatcher - command: ["bash", "-c", "while [ ! -f ~/.config/hypr/settings.json ]; do sleep 1; done; inotifywait -qq -e modify,close_write ~/.config/hypr/settings.json"] + command: ["bash", "-c", "while [ ! -f ~/.config/hypr/settings.json ]; do sleep 1; done; exec inotifywait -qq -e modify,close_write ~/.config/hypr/settings.json"] running: true stdout: StdioCollector { onStreamFinished: { @@ -291,7 +305,7 @@ Variants { Process { id: wsDaemon - command: ["bash", "-c", "~/.config/hypr/scripts/workspaces.sh"] + command: ["bash", "-c", "exec ~/.config/hypr/scripts/workspaces.sh"] running: true } @@ -340,7 +354,7 @@ Variants { Process { id: wsWatcher running: true - command: ["bash", "-c", "inotifywait -qq -e close_write,modify " + paths.getRunDir("workspaces") + "/workspaces.json"] + command: ["bash", "-c", "exec inotifywait -qq -e close_write,modify " + paths.getRunDir("workspaces") + "/workspaces.json"] onExited: { wsReader.running = false; wsReader.running = true; @@ -414,7 +428,7 @@ Variants { Process { id: mprisWatcher running: true - command: ["bash", "-c", "dbus-monitor --session \"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.mpris.MediaPlayer2.Player'\" \"type='signal',interface='org.mpris.MediaPlayer2.Player',member='Seeked'\" 2>/dev/null | grep -m 1 'member=' > /dev/null || sleep 2"] + command: ["bash", "-c", "exec dbus-monitor --session \"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.mpris.MediaPlayer2.Player'\" \"type='signal',interface='org.mpris.MediaPlayer2.Player',member='Seeked'\" 2>/dev/null | grep -m 1 'member=' > /dev/null || sleep 2"] onExited: { musicForceRefresh.running = false; musicForceRefresh.running = true; @@ -436,7 +450,7 @@ Variants { Process { id: kbPoller; running: true - command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/kb_fetch.sh"] + command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/kb_fetch.sh"] stdout: StdioCollector { onStreamFinished: { let txt = this.text.trim(); @@ -447,11 +461,11 @@ Variants { } } } - Process { id: kbWaiter; command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/kb_wait.sh"]; onExited: { kbPoller.running = false; kbPoller.running = true; } } + Process { id: kbWaiter; command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/kb_wait.sh"]; onExited: { kbPoller.running = false; kbPoller.running = true; } } Process { id: audioPoller; running: true - command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/audio_fetch.sh"] + command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/audio_fetch.sh"] stdout: StdioCollector { onStreamFinished: { let txt = this.text.trim(); @@ -470,11 +484,11 @@ Variants { } } } - Process { id: audioWaiter; command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/audio_wait.sh"]; onExited: { audioPoller.running = false; audioPoller.running = true; } } + Process { id: audioWaiter; command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/audio_wait.sh"]; onExited: { audioPoller.running = false; audioPoller.running = true; } } Process { id: networkPoller; running: true - command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/network_fetch.sh"] + command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/network_fetch.sh"] stdout: StdioCollector { onStreamFinished: { let txt = this.text.trim(); @@ -492,11 +506,11 @@ Variants { } } } - Process { id: networkWaiter; command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/network_wait.sh"]; onExited: { networkPoller.running = false; networkPoller.running = true; } } + Process { id: networkWaiter; command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/network_wait.sh"]; onExited: { networkPoller.running = false; networkPoller.running = true; } } Process { id: btPoller; running: true - command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/bt_fetch.sh"] + command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/bt_fetch.sh"] stdout: StdioCollector { onStreamFinished: { let txt = this.text.trim(); @@ -513,11 +527,11 @@ Variants { } } } - Process { id: btWaiter; command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/bt_wait.sh"]; onExited: { btPoller.running = false; btPoller.running = true; } } + Process { id: btWaiter; command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/bt_wait.sh"]; onExited: { btPoller.running = false; btPoller.running = true; } } Process { id: batteryPoller; running: true - command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/battery_fetch.sh"] + command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/battery_fetch.sh"] stdout: StdioCollector { onStreamFinished: { let txt = this.text.trim(); @@ -535,7 +549,24 @@ Variants { } } } - Process { id: batteryWaiter; command: ["bash", "-c", "~/.config/hypr/scripts/quickshell/watchers/battery_wait.sh"]; onExited: { batteryPoller.running = false; batteryPoller.running = true; } } + Process { id: batteryWaiter; command: ["bash", "-c", "exec ~/.config/hypr/scripts/quickshell/watchers/battery_wait.sh"]; onExited: { batteryPoller.running = false; batteryPoller.running = true; } } + + Process { + id: mouseBatteryPoller; running: true + command: ["bash", "-c", "lsusb -d 046d:c52b >/dev/null 2>&1 && upower -d | awk '/Logitech M510/ {found=1} found && /battery-level:/ {level=$2} found && /percentage:/ {if(level==\"full\") print 100; else if(level==\"good\") print 90; else if(level==\"normal\") print 70; else if(level==\"low\") print 20; else if(level==\"critical\") print 5; else {gsub(\"%\",\"\", $2); print $2}; exit}' || echo 'offline'"] + stdout: StdioCollector { + onStreamFinished: { + let txt = this.text.trim(); + let match = txt.match(/(\d+)/); + if (match) { + barWindow.mouseBatPercent = match[1] + "%"; + } else if (txt === "offline" || txt === "") { + barWindow.mouseBatPercent = ""; + } + } + } + } + Timer { running: true; repeat: true; interval: 15000; onTriggered: { mouseBatteryPoller.running = false; mouseBatteryPoller.running = true; } } Process { id: weatherPoller @@ -1269,14 +1300,15 @@ Variants { width: targetWidth Behavior on width { NumberAnimation { duration: 500; easing.type: Easing.OutQuint } } + id: kbContainer scale: isHovered ? 1.05 : 1.0 Behavior on scale { NumberAnimation { duration: 250; easing.type: Easing.OutExpo } } Behavior on color { ColorAnimation { duration: 200 } } property bool initAnimTrigger: false - Timer { running: rightContent.showLayout && !parent.initAnimTrigger; interval: 0; onTriggered: parent.initAnimTrigger = true } + Timer { running: rightContent.showLayout && !kbContainer.initAnimTrigger; interval: 0; onTriggered: kbContainer.initAnimTrigger = true } opacity: initAnimTrigger ? 1 : 0 - transform: Translate { y: parent.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } + transform: Translate { y: kbContainer.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } Behavior on opacity { NumberAnimation { duration: 400; easing.type: Easing.OutCubic } } Row { @@ -1319,9 +1351,9 @@ Variants { Behavior on color { ColorAnimation { duration: 200 } } property bool initAnimTrigger: false - Timer { running: rightContent.showLayout && !parent.initAnimTrigger; interval: 50; onTriggered: parent.initAnimTrigger = true } + Timer { running: rightContent.showLayout && !wifiPill.initAnimTrigger; interval: 50; onTriggered: wifiPill.initAnimTrigger = true } opacity: initAnimTrigger ? 1 : 0 - transform: Translate { y: parent.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } + transform: Translate { y: wifiPill.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } Behavior on opacity { NumberAnimation { duration: 400; easing.type: Easing.OutCubic } } Row { @@ -1378,9 +1410,9 @@ Variants { Behavior on color { ColorAnimation { duration: 200 } } property bool initAnimTrigger: false - Timer { running: rightContent.showLayout && !parent.initAnimTrigger; interval: 100; onTriggered: parent.initAnimTrigger = true } + Timer { running: rightContent.showLayout && !btPill.initAnimTrigger; interval: 100; onTriggered: btPill.initAnimTrigger = true } opacity: initAnimTrigger ? 1 : 0 - transform: Translate { y: parent.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } + transform: Translate { y: btPill.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } Behavior on opacity { NumberAnimation { duration: 400; easing.type: Easing.OutCubic } } Row { @@ -1425,14 +1457,15 @@ Variants { width: targetWidth Behavior on width { NumberAnimation { duration: 500; easing.type: Easing.OutQuint } } + id: volItem scale: isHovered ? 1.05 : 1.0 Behavior on scale { NumberAnimation { duration: 250; easing.type: Easing.OutExpo } } Behavior on color { ColorAnimation { duration: 200 } } property bool initAnimTrigger: false - Timer { running: rightContent.showLayout && !parent.initAnimTrigger; interval: 150; onTriggered: parent.initAnimTrigger = true } + Timer { running: rightContent.showLayout && !volItem.initAnimTrigger; interval: 150; onTriggered: volItem.initAnimTrigger = true } opacity: initAnimTrigger ? 1 : 0 - transform: Translate { y: parent.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } + transform: Translate { y: volItem.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } Behavior on opacity { NumberAnimation { duration: 400; easing.type: Easing.OutCubic } } Row { @@ -1474,18 +1507,19 @@ Variants { } } - property real targetWidth: barWindow.isDesktop ? barWindow.s(34) : batLayoutRow.implicitWidth + barWindow.s(24) + property real targetWidth: (barWindow.isDesktop && barWindow.mouseBatPercent === "") ? barWindow.s(34) : batLayoutRow.implicitWidth + barWindow.s(24) width: targetWidth Behavior on width { NumberAnimation { duration: 500; easing.type: Easing.OutQuint } } + id: batItem scale: isHovered ? 1.05 : 1.0 Behavior on scale { NumberAnimation { duration: 250; easing.type: Easing.OutExpo } } Behavior on color { ColorAnimation { duration: 200 } } property bool initAnimTrigger: false - Timer { running: rightContent.showLayout && !parent.initAnimTrigger; interval: 200; onTriggered: parent.initAnimTrigger = true } + Timer { running: rightContent.showLayout && !batItem.initAnimTrigger; interval: 200; onTriggered: batItem.initAnimTrigger = true } opacity: initAnimTrigger ? 1 : 0 - transform: Translate { y: parent.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } + transform: Translate { y: batItem.initAnimTrigger ? 0 : barWindow.s(15); Behavior on y { NumberAnimation { duration: 500; easing.type: Easing.OutBack } } } Behavior on opacity { NumberAnimation { duration: 400; easing.type: Easing.OutCubic } } Row { @@ -1506,6 +1540,20 @@ Variants { color: mocha.base Behavior on color { ColorAnimation { duration: 300 } } } + Text { + anchors.verticalCenter: parent.verticalCenter + visible: barWindow.mouseBatPercent !== "" + text: "󰍽"; font.family: "Iosevka Nerd Font"; font.pixelSize: barWindow.s(16); + color: mocha.base + Behavior on color { ColorAnimation { duration: 300 } } + } + Text { + anchors.verticalCenter: parent.verticalCenter + visible: barWindow.mouseBatPercent !== "" + text: barWindow.mouseBatPercent; font.family: "JetBrains Mono"; font.pixelSize: barWindow.s(13); font.weight: Font.Black; + color: mocha.base + Behavior on color { ColorAnimation { duration: 300 } } + } } MouseArea { id: batMouse; hoverEnabled: true; anchors.fill: parent; onClicked: Quickshell.execDetached(["bash", "-c", "~/.config/hypr/scripts/qs_manager.sh toggle battery"]) } } diff --git a/.config/hypr/scripts/quickshell/movies/MovieWidget.qml b/.config/hypr/scripts/quickshell/movies/MovieWidget.qml index 3ab4e27..5842476 100644 --- a/.config/hypr/scripts/quickshell/movies/MovieWidget.qml +++ b/.config/hypr/scripts/quickshell/movies/MovieWidget.qml @@ -304,16 +304,7 @@ Item { // --- SOURCE MODEL --- ListModel { id: sourceModel - ListElement { name: "VidSrc.net"; urlMovie: "https://vidsrc.net/embed/movie/%1"; urlTv: "https://vidsrc.net/embed/tv/%1/%2/%3"; status: "pending" } - ListElement { name: "VidLink"; urlMovie: "https://vidlink.pro/movie/%1?autoplay=1"; urlTv: "https://vidlink.pro/tv/%1/%2/%3?autoplay=1"; status: "pending" } - ListElement { name: "VidSrc.pro"; urlMovie: "https://vidsrc.pro/embed/movie/%1"; urlTv: "https://vidsrc.pro/embed/tv/%1/%2/%3"; status: "pending" } - ListElement { name: "VidSrc.in"; urlMovie: "https://vidsrc.in/embed/movie/%1"; urlTv: "https://vidsrc.in/embed/tv/%1/%2/%3"; status: "pending" } - ListElement { name: "VidSrc.cc"; urlMovie: "https://vidsrc.cc/v2/embed/movie/%1?autoPlay=true"; urlTv: "https://vidsrc.cc/v2/embed/tv/%1/%2/%3?autoPlay=true"; status: "pending" } - ListElement { name: "Embed.su"; urlMovie: "https://embed.su/embed/movie/%1"; urlTv: "https://embed.su/embed/tv/%1/%2/%3"; status: "pending" } - ListElement { name: "SmashyStream"; urlMovie: "https://player.smashy.stream/movie/%1"; urlTv: "https://player.smashy.stream/tv/%1?s=%2&e=%3"; status: "pending" } - ListElement { name: "AutoEmbed"; urlMovie: "https://autoembed.to/movie/imdb/%1"; urlTv: "https://autoembed.to/tv/imdb/%1-%2-%3"; status: "pending" } - ListElement { name: "2Embed"; urlMovie: "https://www.2embed.cc/embed/%1"; urlTv: "https://www.2embed.cc/embedtv/%1&s=%2&e=%3"; status: "pending" } - ListElement { name: "MultiEmbed"; urlMovie: "https://multiembed.mov/directstream.php?video_id=%1"; urlTv: "https://multiembed.mov/directstream.php?video_id=%1&s=%2&e=%3"; status: "pending" } + ListElement { name: "Stremio"; urlMovie: "stremio://detail/movie/%1/%1"; urlTv: "stremio://detail/series/%1/%1:%2:%3"; status: "pending" } } // --- ANIMATIONS & FOCUS --- @@ -545,6 +536,15 @@ Item { let idx = window.currentCheckIndex let step = window.sourceCheckStep let url = buildSourceUrl(idx) + if (url.startsWith("stremio://")) { + sourceModel.setProperty(idx, "status", "success") + window.foundSourceName = sourceModel.get(idx).name + window.checkingState = "found" + saveUiState() + Quickshell.execDetached(["xdg-open", url]) + return + } + let xhr = new XMLHttpRequest() window.activeCheckXhr = xhr