diff --git a/.github/README.md b/.github/README.md index 12d645adf..2020ab82b 100644 --- a/.github/README.md +++ b/.github/README.md @@ -78,7 +78,7 @@ Widget system: Quickshell | Support: Yes | AI, settings app | Some widgets | |:---|:---------------| -| image | image | +| image | image | | Window management | Weeb power | | image | image | diff --git a/.gitignore b/.gitignore index bc6ebed73..cef4d7aab 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ __pycache__/ *.py[cod] dots/.config/quickshell/ii/.qmlls.ini .update-lock +/os-release diff --git a/diagnose b/diagnose index 408b5e896..a686cab5e 100755 --- a/diagnose +++ b/diagnose @@ -58,6 +58,9 @@ ii_check_venv() { ii_check_quickshell_version() { pacman -Q | grep -E 'quickshell|qt6-base' } +ii_check_PKGBUILD_version() { + pacman -Q | grep '^illogical-impulse-' +} e "Checking git repo info" x git remote get-url origin @@ -88,6 +91,7 @@ x ls -l ~/.local/state/quickshell/.venv e "Checking versions" x Hyprland --version x ii_check_quickshell_version +x ii_check_PKGBUILD_version e "Finished. Output saved as \"$output_file\"." if ! command -v curl 2>&1 >>/dev/null ;then echo "\"curl\" not found, pastebin upload unavailable.";exit;fi diff --git a/dots-extra/vianix/README.md b/dots-extra/vianix/README.md new file mode 100644 index 000000000..5f77637f9 --- /dev/null +++ b/dots-extra/vianix/README.md @@ -0,0 +1 @@ +This folder contains tweakd configs when --via-nix is specified. diff --git a/dots-extra/vianix/hypridle.conf b/dots-extra/vianix/hypridle.conf new file mode 100644 index 000000000..6f1858607 --- /dev/null +++ b/dots-extra/vianix/hypridle.conf @@ -0,0 +1,26 @@ +$lock_cmd = swaylock +# $lock_cmd = pidof hyprlock || hyprlock +$suspend_cmd = systemctl suspend || loginctl suspend + +general { + lock_cmd = $lock_cmd + before_sleep_cmd = loginctl lock-session + after_sleep_cmd = hyprctl dispatch global quickshell:lockFocus + inhibit_sleep = 3 +} + +listener { + timeout = 300 # 5mins + on-timeout = loginctl lock-session +} + +listener { + timeout = 600 # 10mins + on-timeout = hyprctl dispatch dpms off + on-resume = hyprctl dispatch dpms on +} + +listener { + timeout = 900 # 15mins + on-timeout = $suspend_cmd +} diff --git a/dots/.config/hypr/hyprland/keybinds.conf b/dots/.config/hypr/hyprland/keybinds.conf index 263d5048d..b77166eb2 100644 --- a/dots/.config/hypr/hyprland/keybinds.conf +++ b/dots/.config/hypr/hyprland/keybinds.conf @@ -41,7 +41,7 @@ bind = Shift+Super+Alt, Slash, exec, qs -p ~/.config/quickshell/$qsConfig/welcom bindle=, XF86MonBrightnessUp, exec, qs -c $qsConfig ipc call brightness increment || brightnessctl s 5%+ # [hidden] bindle=, XF86MonBrightnessDown, exec, qs -c $qsConfig ipc call brightness decrement || brightnessctl s 5%- # [hidden] -bindle=, XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%+ # [hidden] +bindle=, XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%+ -l 1.5# [hidden] bindle=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%- # [hidden] bindl = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SINK@ toggle # [hidden] diff --git a/dots/.config/hypr/hyprland/rules.conf b/dots/.config/hypr/hyprland/rules.conf index 4891ee854..7eb985c94 100644 --- a/dots/.config/hypr/hyprland/rules.conf +++ b/dots/.config/hypr/hyprland/rules.conf @@ -144,6 +144,7 @@ layerrule = animation slide bottom, quickshell:osk layerrule = noanim, quickshell:polkit layerrule = xray 0, quickshell:popup # No weird color for bar tooltips (this in theory should suffice) layerrule = ignorealpha 1, quickshell:popup # No weird color for bar tooltips (but somehow this is necessary) +layerrule = ignorealpha 1, quickshell:mediaControls # Same as above layerrule = noanim, quickshell:regionSelector layerrule = noanim, quickshell:screenshot layerrule = blur, quickshell:session diff --git a/dots/.config/hypr/hyprland/scripts/start_geoclue_agent.sh b/dots/.config/hypr/hyprland/scripts/start_geoclue_agent.sh index e464d0531..b4b8fa14d 100755 --- a/dots/.config/hypr/hyprland/scripts/start_geoclue_agent.sh +++ b/dots/.config/hypr/hyprland/scripts/start_geoclue_agent.sh @@ -7,13 +7,16 @@ if pgrep -f 'geoclue-2.0/demos/agent' > /dev/null; then fi # List of known possible GeoClue agent paths -AGENT_PATHS=" -/usr/libexec/geoclue-2.0/demos/agent -/usr/lib/geoclue-2.0/demos/agent -" +AGENT_PATHS=( + /usr/libexec/geoclue-2.0/demos/agent + /usr/lib/geoclue-2.0/demos/agent + "$HOME/.nix-profile/libexec/geoclue-2.0/demos/agent" + "$HOME/.nix-profile/lib/geoclue-2.0/demos/agent" + /run/current-system/sw/libexec/geoclue-2.0/demos/agent +) # Find the first valid agent path -for path in $AGENT_PATHS; do +for path in "${AGENT_PATHS[@]}"; do if [ -x "$path" ]; then echo "Starting GeoClue agent from: $path" "$path" & # starts in the background diff --git a/dots/.config/quickshell/ii/modules/background/Background.qml b/dots/.config/quickshell/ii/modules/background/Background.qml index 9209450be..f36cf3590 100644 --- a/dots/.config/quickshell/ii/modules/background/Background.qml +++ b/dots/.config/quickshell/ii/modules/background/Background.qml @@ -334,7 +334,6 @@ Variants { font { pixelSize: Appearance.font.pixelSize.normal weight: 350 - italic: true } color: bgRoot.colText style: Text.Raised diff --git a/dots/.config/quickshell/ii/modules/bar/Workspaces.qml b/dots/.config/quickshell/ii/modules/bar/Workspaces.qml index f1da2dab3..f0a61ef23 100644 --- a/dots/.config/quickshell/ii/modules/bar/Workspaces.qml +++ b/dots/.config/quickshell/ii/modules/bar/Workspaces.qml @@ -1,6 +1,7 @@ import qs import qs.services import qs.modules.common +import qs.modules.common.models import qs.modules.common.widgets import qs.modules.common.functions import QtQuick @@ -163,12 +164,12 @@ Item { horizontalCenter: vertical ? parent.horizontalCenter : undefined } - // idx1 is the "leading" indicator position, idx2 is the "following" one - // The former animates faster than the latter, see the NumberAnimations below - property real idx1: workspaceIndexInGroup - property real idx2: workspaceIndexInGroup - property real indicatorPosition: Math.min(idx1, idx2) * workspaceButtonWidth + root.activeWorkspaceMargin - property real indicatorLength: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2 + AnimatedTabIndexPair { + id: idxPair + index: root.workspaceIndexInGroup + } + property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * workspaceButtonWidth + root.activeWorkspaceMargin + property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2 property real indicatorThickness: workspaceButtonWidth - root.activeWorkspaceMargin * 2 x: root.vertical ? null : indicatorPosition @@ -176,18 +177,6 @@ Item { y: root.vertical ? indicatorPosition : null implicitHeight: root.vertical ? indicatorLength : indicatorThickness - Behavior on idx1 { - NumberAnimation { - duration: 100 - easing.type: Easing.OutSine - } - } - Behavior on idx2 { - NumberAnimation { - duration: 300 - easing.type: Easing.OutSine - } - } } // Workspaces - numbers diff --git a/dots/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml b/dots/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml index fb9e55367..f44efcb3e 100644 --- a/dots/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml +++ b/dots/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml @@ -31,7 +31,6 @@ Scope { // Scope sourceComponent: PanelWindow { // Window id: cheatsheetRoot visible: cheatsheetLoader.active - property int selectedTab: 0 anchors { top: true @@ -76,7 +75,7 @@ Scope { // Scope border.width: 1 border.color: Appearance.colors.colLayer0Border radius: Appearance.rounding.windowRounding - property real padding: 30 + property real padding: 20 implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2 implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2 @@ -86,16 +85,16 @@ Scope { // Scope } if (event.modifiers === Qt.ControlModifier) { if (event.key === Qt.Key_PageDown) { - cheatsheetRoot.selectedTab = Math.min(cheatsheetRoot.selectedTab + 1, root.tabButtonList.length - 1); + tabBar.incrementCurrentIndex(); event.accepted = true; } else if (event.key === Qt.Key_PageUp) { - cheatsheetRoot.selectedTab = Math.max(cheatsheetRoot.selectedTab - 1, 0); + tabBar.decrementCurrentIndex(); event.accepted = true; } else if (event.key === Qt.Key_Tab) { - cheatsheetRoot.selectedTab = (cheatsheetRoot.selectedTab + 1) % root.tabButtonList.length; + tabBar.setCurrentIndex((tabBar.currentIndex + 1) % root.tabButtonList.length); event.accepted = true; } else if (event.key === Qt.Key_Backtab) { - cheatsheetRoot.selectedTab = (cheatsheetRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length; + tabBar.setCurrentIndex((tabBar.currentIndex - 1 + root.tabButtonList.length) % root.tabButtonList.length); event.accepted = true; } } @@ -129,23 +128,15 @@ Scope { // Scope ColumnLayout { // Real content id: cheatsheetColumnLayout anchors.centerIn: parent - spacing: 20 + spacing: 10 - StyledText { - id: cheatsheetTitle + Toolbar { Layout.alignment: Qt.AlignHCenter - font { - family: Appearance.font.family.title - pixelSize: Appearance.font.pixelSize.title - variableAxes: Appearance.font.variableAxes.title - } - text: Translation.tr("Cheat sheet") - } - PrimaryTabBar { // Tab strip - id: tabBar - tabButtonList: root.tabButtonList - Synchronizer on currentIndex { - property alias source: cheatsheetRoot.selectedTab + enableShadow: false + ToolbarTabBar { + id: tabBar + tabButtonList: root.tabButtonList + currentIndex: swipeView.currentIndex } } @@ -154,26 +145,11 @@ Scope { // Scope Layout.topMargin: 5 Layout.fillWidth: true Layout.fillHeight: true + currentIndex: tabBar.currentIndex spacing: 10 - Behavior on implicitWidth { - id: contentWidthBehavior - enabled: false - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on implicitHeight { - id: contentHeightBehavior - enabled: false - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - - currentIndex: cheatsheetRoot.selectedTab - onCurrentIndexChanged: { - contentWidthBehavior.enabled = true; - contentHeightBehavior.enabled = true; - tabBar.enableIndicatorAnimation = true; - cheatsheetRoot.selectedTab = currentIndex; - } + implicitWidth: Math.max.apply(null, contentChildren.map(child => child.implicitWidth || 0)) + implicitHeight: Math.max.apply(null, contentChildren.map(child => child.implicitHeight || 0)) clip: true layer.enabled: true diff --git a/dots/.config/quickshell/ii/modules/cheatsheet/CheatsheetPeriodicTable.qml b/dots/.config/quickshell/ii/modules/cheatsheet/CheatsheetPeriodicTable.qml index 158e707d3..6262ed77a 100644 --- a/dots/.config/quickshell/ii/modules/cheatsheet/CheatsheetPeriodicTable.qml +++ b/dots/.config/quickshell/ii/modules/cheatsheet/CheatsheetPeriodicTable.qml @@ -11,6 +11,7 @@ Item { Column { id: mainLayout + anchors.centerIn: parent spacing: root.spacing Repeater { // Main table rows diff --git a/dots/.config/quickshell/ii/modules/cheatsheet/ElementTile.qml b/dots/.config/quickshell/ii/modules/cheatsheet/ElementTile.qml index d84abedfd..e05b76a9c 100644 --- a/dots/.config/quickshell/ii/modules/cheatsheet/ElementTile.qml +++ b/dots/.config/quickshell/ii/modules/cheatsheet/ElementTile.qml @@ -7,8 +7,8 @@ RippleButton { id: root required property var element opacity: element.type != "empty" ? 1 : 0 - implicitHeight: 60 - implicitWidth: 60 + implicitHeight: 70 + implicitWidth: 70 colBackground: Appearance.colors.colLayer2 buttonRadius: Appearance.rounding.small diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index c4af0a850..766033e47 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -373,6 +373,7 @@ Singleton { property real scale: 0.18 // Relative to screen size property real rows: 2 property real columns: 5 + property bool centerIcons: true } property JsonObject regionSelector: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/common/models/AnimatedTabIndexPair.qml b/dots/.config/quickshell/ii/modules/common/models/AnimatedTabIndexPair.qml new file mode 100644 index 000000000..c18e9ccf2 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/models/AnimatedTabIndexPair.qml @@ -0,0 +1,26 @@ +import QtQuick + +// idx1 is the "leading" indicator position, idx2 is the "following" one +// The former animates faster than the latter, see the NumberAnimations below +QtObject { + id: root + required property int index + + property real idx1: index + property real idx2: index + property int idx1Duration: 100 + property int idx2Duration: 300 + + Behavior on idx1 { + NumberAnimation { + duration: root.idx1Duration + easing.type: Easing.OutSine + } + } + Behavior on idx2 { + NumberAnimation { + duration: root.idx2Duration + easing.type: Easing.OutSine + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/IconAndTextToolbarButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/IconAndTextToolbarButton.qml index 45f90f8a1..875aec400 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/IconAndTextToolbarButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/IconAndTextToolbarButton.qml @@ -13,7 +13,7 @@ ToolbarButton { contentItem: Row { anchors.centerIn: parent - spacing: 6 + spacing: 4 MaterialSymbol { anchors.verticalCenter: parent.verticalCenter diff --git a/dots/.config/quickshell/ii/modules/common/widgets/PrimaryTabBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/PrimaryTabBar.qml deleted file mode 100644 index 474bdc591..000000000 --- a/dots/.config/quickshell/ii/modules/common/widgets/PrimaryTabBar.qml +++ /dev/null @@ -1,95 +0,0 @@ -import qs.modules.common -import qs.services -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Qt.labs.synchronizer - -ColumnLayout { - id: root - spacing: 0 - required property var tabButtonList // Something like [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}] - property int currentIndex - property bool enableIndicatorAnimation: false - property color colIndicator: Appearance?.colors.colPrimary ?? "#65558F" - property color colBorder: Appearance?.m3colors.m3outlineVariant ?? "#C6C6D0" - - onCurrentIndexChanged: { - enableIndicatorAnimation = true - } - - property bool centerTabBar: parent.width > 500 - Layout.fillWidth: !centerTabBar - Layout.alignment: Qt.AlignHCenter - implicitWidth: Math.max(tabBar.implicitWidth, 600) - - TabBar { - id: tabBar - Layout.fillWidth: true - Synchronizer on currentIndex { - property alias source: root.currentIndex - } - - background: Item { - WheelHandler { - onWheel: (event) => { - if (event.angleDelta.y < 0) - tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1) - else if (event.angleDelta.y > 0) - tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0) - } - acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad - } - } - - Repeater { - model: root.tabButtonList - delegate: PrimaryTabButton { - selected: (index == root.currentIndex) - buttonText: modelData.name - buttonIcon: modelData.icon - minimumWidth: 160 - onClicked: root.currentIndex = index - } - } - } - - Item { // Tab indicator - id: tabIndicator - Layout.fillWidth: true - height: 3 - - Rectangle { - id: indicator - property int tabCount: root.tabButtonList.length - property real fullTabSize: root.width / tabCount; - property real targetWidth: tabBar.contentItem?.children[0]?.children[tabBar.currentIndex]?.tabContentWidth ?? 0 - - implicitWidth: targetWidth - anchors { - top: parent.top - bottom: parent.bottom - } - - x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2 - - color: root.colIndicator - radius: Appearance?.rounding.full ?? 9999 - - Behavior on x { - animation: Appearance?.animation.elementMove.numberAnimation.createObject(this) - } - - Behavior on implicitWidth { - animation: Appearance?.animation.elementMove.numberAnimation.createObject(this) - } - } - } - - Rectangle { // Tabbar bottom border - id: tabBarBottomBorder - Layout.fillWidth: true - implicitHeight: 1 - color: root.colBorder - } -} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/PrimaryTabButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/PrimaryTabButton.qml deleted file mode 100644 index bedcc2d92..000000000 --- a/dots/.config/quickshell/ii/modules/common/widgets/PrimaryTabButton.qml +++ /dev/null @@ -1,172 +0,0 @@ -import qs.modules.common -import qs.modules.common.widgets -import qs.modules.common.functions -import Qt5Compat.GraphicalEffects -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -TabButton { - id: button - property string buttonText - property string buttonIcon - property real minimumWidth: 110 - property bool selected: false - property int tabContentWidth: contentItem.children[0].implicitWidth - property int rippleDuration: 1200 - height: buttonBackground.height - implicitWidth: Math.max(tabContentWidth, buttonBackground.implicitWidth, minimumWidth) - - property color colBackground: ColorUtils.transparentize(Appearance?.colors.colLayer1Hover, 1) || "transparent" - property color colBackgroundHover: Appearance?.colors.colLayer1Hover ?? "#E5DFED" - property color colRipple: Appearance?.colors.colLayer1Active ?? "#D6CEE2" - property color colActive: Appearance?.colors.colPrimary ?? "#65558F" - property color colInactive: Appearance?.colors.colOnLayer1 ?? "#45464F" - - component RippleAnim: NumberAnimation { - duration: rippleDuration - easing.type: Appearance?.animation.elementMoveEnter.type - easing.bezierCurve: Appearance?.animationCurves.standardDecel - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onPressed: (event) => { - button.click() // Because the MouseArea already consumed the event - const {x,y} = event - const stateY = buttonBackground.y; - rippleAnim.x = x; - rippleAnim.y = y - stateY; - - const dist = (ox,oy) => ox*ox + oy*oy - const stateEndY = stateY + buttonBackground.height - rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY))) - - rippleFadeAnim.complete(); - rippleAnim.restart(); - } - onReleased: (event) => { - rippleFadeAnim.restart(); - } - } - - RippleAnim { - id: rippleFadeAnim - duration: rippleDuration * 2 - target: ripple - property: "opacity" - to: 0 - } - - SequentialAnimation { - id: rippleAnim - - property real x - property real y - property real radius - - PropertyAction { - target: ripple - property: "x" - value: rippleAnim.x - } - PropertyAction { - target: ripple - property: "y" - value: rippleAnim.y - } - PropertyAction { - target: ripple - property: "opacity" - value: 1 - } - ParallelAnimation { - RippleAnim { - target: ripple - properties: "implicitWidth,implicitHeight" - from: 0 - to: rippleAnim.radius * 2 - } - } - } - - background: Rectangle { - id: buttonBackground - radius: Appearance?.rounding.small - implicitHeight: 50 - color: (button.hovered ? button.colBackgroundHover : button.colBackground) - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: buttonBackground.width - height: buttonBackground.height - radius: buttonBackground.radius - } - } - - Behavior on color { - animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this) - } - - Item { - id: ripple - width: ripple.implicitWidth - height: ripple.implicitHeight - opacity: 0 - - property real implicitWidth: 0 - property real implicitHeight: 0 - visible: width > 0 && height > 0 - - Behavior on opacity { - animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this) - } - - RadialGradient { - anchors.fill: parent - gradient: Gradient { - GradientStop { position: 0.0; color: button.colRipple } - GradientStop { position: 0.3; color: button.colRipple } - GradientStop { position: 0.5 ; color: Qt.rgba(button.colRipple.r, button.colRipple.g, button.colRipple.b, 0) } - } - } - - transform: Translate { - x: -ripple.width / 2 - y: -ripple.height / 2 - } - } - } - - contentItem: Item { - anchors.centerIn: buttonBackground - ColumnLayout { - anchors.centerIn: parent - spacing: 0 - MaterialSymbol { - visible: buttonIcon?.length > 0 - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - text: buttonIcon - iconSize: Appearance?.font.pixelSize.hugeass ?? 25 - fill: selected ? 1 : 0 - color: selected ? button.colActive : button.colInactive - Behavior on color { - animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this) - } - } - StyledText { - id: buttonTextWidget - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - font.pixelSize: Appearance?.font.pixelSize.small - color: selected ? button.colActive : button.colInactive - text: buttonText - Behavior on color { - animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this) - } - } - } - } -} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/common/widgets/Toolbar.qml b/dots/.config/quickshell/ii/modules/common/widgets/Toolbar.qml index 85112160f..51348c4fa 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/Toolbar.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/Toolbar.qml @@ -10,6 +10,7 @@ import qs.modules.common.widgets Item { id: root + property bool enableShadow: true property real padding: 8 property alias colBackground: background.color property alias spacing: toolbarLayout.spacing @@ -18,15 +19,20 @@ Item { implicitHeight: background.implicitHeight property alias radius: background.radius - StyledRectangularShadow { - target: background + Loader { + active: root.enableShadow + anchors.fill: background + sourceComponent: StyledRectangularShadow { + target: background + anchors.fill: undefined + } } Rectangle { id: background anchors.fill: parent color: Appearance.m3colors.m3surfaceContainer - implicitHeight: Math.max(toolbarLayout.implicitHeight + root.padding * 2, 56) + implicitHeight: 56 implicitWidth: toolbarLayout.implicitWidth + root.padding * 2 radius: height / 2 diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml new file mode 100644 index 000000000..4d4d56335 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml @@ -0,0 +1,103 @@ +pragma ComponentBehavior: Bound +import qs.modules.common +import qs.modules.common.models +import qs.services +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt.labs.synchronizer + +Item { + id: root + property alias currentIndex: tabBar.currentIndex + required property var tabButtonList + + function incrementCurrentIndex() { + tabBar.incrementCurrentIndex() + } + function decrementCurrentIndex() { + tabBar.decrementCurrentIndex() + } + function setCurrentIndex(index) { + tabBar.setCurrentIndex(index) + } + + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + implicitWidth: contentItem.implicitWidth + implicitHeight: 40 + + Row { + id: contentItem + z: 1 + anchors.centerIn: parent + spacing: 4 + + Repeater { + model: root.tabButtonList + delegate: ToolbarTabButton { + required property int index + required property var modelData + current: index == root.currentIndex + text: modelData.name + materialSymbol: modelData.icon + onClicked: { + root.setCurrentIndex(index) + } + } + } + } + + Rectangle { + id: activeIndicator + z: 0 + color: Appearance.colors.colSecondaryContainer + implicitWidth: contentItem.children[root.currentIndex]?.implicitWidth ?? 0 + implicitHeight: contentItem.children[root.currentIndex]?.implicitHeight ?? 0 + radius: height / 2 + // Animation + property Item targetItem: contentItem.children[root.currentIndex] + AnimatedTabIndexPair { + id: leftBound + idx1Duration: 50 + idx2Duration: 200 + index: activeIndicator.targetItem.x + } + AnimatedTabIndexPair { + id: rightBound + idx1Duration: 50 + idx2Duration: 200 + index: activeIndicator.targetItem.x + activeIndicator.targetItem.width + } + x: Math.min(leftBound.idx1, leftBound.idx2) + width: Math.max(rightBound.idx1, rightBound.idx2) - x + } + + MouseArea { + anchors.fill: parent + z: 2 + acceptedButtons: Qt.NoButton + cursorShape: Qt.PointingHandCursor + onWheel: (event) => { + if (event.angleDelta.y < 0) { + root.incrementCurrentIndex(); + } + else { + root.decrementCurrentIndex(); + } + } + } + + // TabBar doesn't allow tabs to be of different sizes. Literally unusable. + // We use it only for the logic and draw stuff manually + TabBar { + id: tabBar + z: -1 + background: null + Repeater { // This is to fool the TabBar that it has tabs so it does the indices properly + model: root.tabButtonList.length + delegate: TabButton { + background: null + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabButton.qml new file mode 100644 index 000000000..e53518875 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabButton.qml @@ -0,0 +1,40 @@ +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import Qt5Compat.GraphicalEffects +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +RippleButton { + id: root + required property string materialSymbol + required property bool current + horizontalPadding: 10 + + implicitHeight: 40 + implicitWidth: implicitContentWidth + horizontalPadding * 2 + buttonRadius: height / 2 + + colBackground: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer) + colBackgroundHover: ColorUtils.transparentize(Appearance.colors.colOnSurface, current ? 1 : 0.95) + colRipple: ColorUtils.transparentize(Appearance.colors.colOnSurface, 0.95) + + contentItem: Row { + id: contentRow + anchors.centerIn: parent + spacing: 6 + + MaterialSymbol { + id: icon + anchors.verticalCenter: parent.verticalCenter + iconSize: 22 + text: root.materialSymbol + } + StyledText { + id: label + anchors.verticalCenter: parent.verticalCenter + text: root.text + } + } +} diff --git a/dots/.config/quickshell/ii/modules/lock/Lock.qml b/dots/.config/quickshell/ii/modules/lock/Lock.qml index ead478bdc..e5557425b 100644 --- a/dots/.config/quickshell/ii/modules/lock/Lock.qml +++ b/dots/.config/quickshell/ii/modules/lock/Lock.qml @@ -13,10 +13,16 @@ import Quickshell.Hyprland Scope { id: root + Process { + id: unlockKeyringProc + onExited: (exitCode, exitStatus) => { + KeyringStorage.fetchKeyringData(); + } + } function unlockKeyring() { - Quickshell.execDetached({ + unlockKeyringProc.exec({ environment: ({ - UNLOCK_PASSWORD: root.currentText + "UNLOCK_PASSWORD": lockContext.currentText }), command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")] }) @@ -24,7 +30,7 @@ Scope { property var windowData: [] function saveWindowPositionAndTile() { - Hyprland.dispatch(`keyword dwindle:pseudotile true`) + Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "true"]) root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id)) root.windowData.forEach(w => { Hyprland.dispatch(`pseudo address:${w.address}`) @@ -38,7 +44,7 @@ Scope { Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`) Hyprland.dispatch(`pseudo address:${w.address}`) }) - Hyprland.dispatch(`keyword dwindle:pseudotile false`) + Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "false"]) } // This stores all the information shared between the lock surfaces on each screen. @@ -156,20 +162,24 @@ Scope { } } + function initIfReady() { + if (!Config.ready || !Persistent.ready) return; + if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) { + Hyprland.dispatch("global quickshell:lock") + } else { + KeyringStorage.fetchKeyringData(); + } + } Connections { target: Config function onReadyChanged() { - if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) { - Hyprland.dispatch("global quickshell:lock") - } + root.initIfReady(); } } Connections { target: Persistent function onReadyChanged() { - if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) { - Hyprland.dispatch("global quickshell:lock") - } + root.initIfReady(); } } } diff --git a/dots/.config/quickshell/ii/modules/lock/LockContext.qml b/dots/.config/quickshell/ii/modules/lock/LockContext.qml index b3b7fece2..17893bb18 100644 --- a/dots/.config/quickshell/ii/modules/lock/LockContext.qml +++ b/dots/.config/quickshell/ii/modules/lock/LockContext.qml @@ -38,6 +38,7 @@ Scope { root.resetTargetAction(); root.clearText(); root.unlockInProgress = false; + stopFingerPam(); } Timer { @@ -69,7 +70,9 @@ Scope { } function stopFingerPam() { - fingerPam.abort(); + if (fingerPam.running) { + fingerPam.abort(); + } } Process { diff --git a/dots/.config/quickshell/ii/modules/lock/LockSurface.qml b/dots/.config/quickshell/ii/modules/lock/LockSurface.qml index 4fc147de4..526a7d3b4 100644 --- a/dots/.config/quickshell/ii/modules/lock/LockSurface.qml +++ b/dots/.config/quickshell/ii/modules/lock/LockSurface.qml @@ -119,6 +119,7 @@ MouseArea { ToolbarTextField { id: passwordBox + Layout.rightMargin: -Layout.leftMargin placeholderText: GlobalStates.screenUnlockFailed ? Translation.tr("Incorrect password") : Translation.tr("Enter password") // Style @@ -156,11 +157,11 @@ MouseArea { // Shake when wrong password SequentialAnimation { id: wrongPasswordShakeAnim - NumberAnimation { target: passwordBox; property: "x"; to: -30; duration: 50 } - NumberAnimation { target: passwordBox; property: "x"; to: 30; duration: 50 } - NumberAnimation { target: passwordBox; property: "x"; to: -15; duration: 40 } - NumberAnimation { target: passwordBox; property: "x"; to: 15; duration: 40 } - NumberAnimation { target: passwordBox; property: "x"; to: 0; duration: 30 } + NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: -30; duration: 50 } + NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: 30; duration: 50 } + NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: -15; duration: 40 } + NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: 15; duration: 40 } + NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: 0; duration: 30 } } Connections { target: GlobalStates diff --git a/dots/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml b/dots/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml index 83bc10a22..f50769a51 100644 --- a/dots/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml +++ b/dots/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml @@ -101,7 +101,7 @@ Item { // Player instance id: background anchors.fill: parent anchors.margins: Appearance.sizes.elevationMargin - color: blendedColors.colLayer0 + color: ColorUtils.applyAlpha(blendedColors.colLayer0, 1) radius: root.radius layer.enabled: true diff --git a/dots/.config/quickshell/ii/modules/overview/OverviewWindow.qml b/dots/.config/quickshell/ii/modules/overview/OverviewWindow.qml index e531c4b8c..a05fde09b 100644 --- a/dots/.config/quickshell/ii/modules/overview/OverviewWindow.qml +++ b/dots/.config/quickshell/ii/modules/overview/OverviewWindow.qml @@ -43,10 +43,12 @@ Item { // Window property bool hovered: false property bool pressed: false - property var iconToWindowRatio: 0.35 - property var xwaylandIndicatorToIconRatio: 0.35 - property var iconToWindowRatioCompact: 0.6 - property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing") + property bool centerIcons: Config.options.overview.centerIcons + property real iconGapRatio: 0.06 + property real iconToWindowRatio: centerIcons ? 0.35 : 0.15 + property real xwaylandIndicatorToIconRatio: 0.35 + property real iconToWindowRatioCompact: 0.6 + property string iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing") property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth property bool indicateXWayland: windowData?.xwayland ?? false @@ -109,14 +111,20 @@ Item { // Window Image { id: windowIcon - anchors.centerIn: parent + property real baseSize: Math.min(root.targetWindowWidth, root.targetWindowHeight) + anchors { + top: root.centerIcons ? undefined : parent.top + left: root.centerIcons ? undefined : parent.left + centerIn: root.centerIcons ? parent : undefined + margins: baseSize * root.iconGapRatio + } property var iconSize: { // console.log("-=-=-", root.toplevel.title, "-=-=-") // console.log("Target window size:", targetWindowWidth, targetWindowHeight) // console.log("Icon ratio:", root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) // console.log("Scale:", root.monitorData.scale) // console.log("Final:", Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / root.monitorData.scale) - return Math.min(root.targetWindowWidth, root.targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio); + return baseSize * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio); } // mipmap: true Layout.alignment: Qt.AlignHCenter diff --git a/dots/.config/quickshell/ii/modules/regionSelector/OptionsToolbar.qml b/dots/.config/quickshell/ii/modules/regionSelector/OptionsToolbar.qml index d4e54ddda..63a3e8c7e 100644 --- a/dots/.config/quickshell/ii/modules/regionSelector/OptionsToolbar.qml +++ b/dots/.config/quickshell/ii/modules/regionSelector/OptionsToolbar.qml @@ -65,18 +65,16 @@ Toolbar { } } - IconAndTextToolbarButton { - iconText: "activity_zone" - text: Translation.tr("Rect") - toggled: root.selectionMode === RegionSelection.SelectionMode.RectCorners - onClicked: root.selectionMode = RegionSelection.SelectionMode.RectCorners - } - - IconAndTextToolbarButton { - iconText: "gesture" - text: Translation.tr("Circle") - toggled: root.selectionMode === RegionSelection.SelectionMode.Circle - onClicked: root.selectionMode = RegionSelection.SelectionMode.Circle + ToolbarTabBar { + id: tabBar + tabButtonList: [ + {"icon": "activity_zone", "name": Translation.tr("Rect")}, + {"icon": "gesture", "name": Translation.tr("Circle")} + ] + currentIndex: root.selectionMode === RegionSelection.SelectionMode.RectCorners ? 0 : 1 + onCurrentIndexChanged: { + root.selectionMode = currentIndex === 0 ? RegionSelection.SelectionMode.RectCorners : RegionSelection.SelectionMode.Circle; + } } } diff --git a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml index 5ecce47ba..a0853cc89 100644 --- a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml @@ -17,7 +17,7 @@ PanelWindow { color: "transparent" WlrLayershell.namespace: "quickshell:regionSelector" WlrLayershell.layer: WlrLayer.Overlay - WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand exclusionMode: ExclusionMode.Ignore anchors { left: true diff --git a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index b1d51063b..90aa11030 100644 --- a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -961,6 +961,14 @@ ContentPage { Config.options.overview.enable = checked; } } + ConfigSwitch { + buttonIcon: "center_focus_strong" + text: Translation.tr("Center icons") + checked: Config.options.overview.centerIcons + onCheckedChanged: { + Config.options.overview.centerIcons = checked; + } + } ConfigSpinBox { icon: "loupe" text: Translation.tr("Scale (%)") diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index eff0483d3..e4ded1eeb 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -13,27 +13,28 @@ import Quickshell.Io Item { id: root + property real padding: 4 property var inputField: messageInputField property string commandPrefix: "/" property var suggestionQuery: "" property var suggestionList: [] - onFocusChanged: (focus) => { + onFocusChanged: focus => { if (focus) { - root.inputField.forceActiveFocus() + root.inputField.forceActiveFocus(); } } - Keys.onPressed: (event) => { - messageInputField.forceActiveFocus() + Keys.onPressed: event => { + messageInputField.forceActiveFocus(); if (event.modifiers === Qt.NoModifier) { if (event.key === Qt.Key_PageUp) { - messageListView.contentY = Math.max(0, messageListView.contentY - messageListView.height / 2) - event.accepted = true + messageListView.contentY = Math.max(0, messageListView.contentY - messageListView.height / 2); + event.accepted = true; } else if (event.key === Qt.Key_PageDown) { - messageListView.contentY = Math.min(messageListView.contentHeight - messageListView.height / 2, messageListView.contentY + messageListView.height / 2) - event.accepted = true + messageListView.contentY = Math.min(messageListView.contentHeight - messageListView.height / 2, messageListView.contentY + messageListView.height / 2); + event.accepted = true; } } if ((event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier) && event.key === Qt.Key_O) { @@ -45,21 +46,21 @@ Item { { name: "attach", description: Translation.tr("Attach a file. Only works with Gemini."), - execute: (args) => { + execute: args => { Ai.attachFile(args.join(" ").trim()); } }, { name: "model", description: Translation.tr("Choose model"), - execute: (args) => { + execute: args => { Ai.setModel(args[0]); } }, { name: "tool", description: Translation.tr("Set the tool to use for the model."), - execute: (args) => { + execute: args => { // console.log(args) if (args.length == 0 || args[0] == "get") { Ai.addMessage(Translation.tr("Usage: %1tool TOOL_NAME").arg(root.commandPrefix), Ai.interfaceRole); @@ -75,7 +76,7 @@ Item { { name: "prompt", description: Translation.tr("Set the system prompt for the model."), - execute: (args) => { + execute: args => { if (args.length === 0 || args[0] === "get") { Ai.printPrompt(); return; @@ -86,9 +87,9 @@ Item { { name: "key", description: Translation.tr("Set API key"), - execute: (args) => { + execute: args => { if (args[0] == "get") { - Ai.printApiKey() + Ai.printApiKey(); } else { Ai.setApiKey(args[0]); } @@ -97,25 +98,25 @@ Item { { name: "save", description: Translation.tr("Save chat"), - execute: (args) => { - const joinedArgs = args.join(" ") + execute: args => { + const joinedArgs = args.join(" "); if (joinedArgs.trim().length == 0) { Ai.addMessage(Translation.tr("Usage: %1save CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole); return; } - Ai.saveChat(joinedArgs) + Ai.saveChat(joinedArgs); } }, { name: "load", description: Translation.tr("Load chat"), - execute: (args) => { - const joinedArgs = args.join(" ") + execute: args => { + const joinedArgs = args.join(" "); if (joinedArgs.trim().length == 0) { Ai.addMessage(Translation.tr("Usage: %1load CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole); return; } - Ai.loadChat(joinedArgs) + Ai.loadChat(joinedArgs); } }, { @@ -128,10 +129,10 @@ Item { { name: "temp", description: Translation.tr("Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5."), - execute: (args) => { + execute: args => { // console.log(args) if (args.length == 0 || args[0] == "get") { - Ai.printTemperature() + Ai.printTemperature(); } else { const temp = parseFloat(args[0]); Ai.setTemperature(temp); @@ -191,8 +192,7 @@ Inline w/ double dollar signs: $$\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\p Inline w/ backslash and square brackets \\[\\int_0^\\infty \\frac{1}{x^2} dx = \\infty\\] Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) -`, - Ai.interfaceRole); +`, Ai.interfaceRole); } }, ] @@ -208,13 +208,12 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) } else { Ai.addMessage(Translation.tr("Unknown command: ") + command, Ai.interfaceRole); } - } - else { + } else { Ai.sendUserMessage(inputText); } - + // Always scroll to bottom when user sends a message - messageListView.positionViewAtEnd() + messageListView.positionViewAtEnd(); } Process { @@ -223,16 +222,14 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) property string imageDecodeFileName: "image" property string imageDecodeFilePath: `${imageDecodePath}/${imageDecodeFileName}` function handleEntry(entry: string) { - imageDecodeFileName = parseInt(entry.match(/^(\d+)\t/)[1]) - decodeImageAndAttachProc.exec(["bash", "-c", - `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'` - ]) + imageDecodeFileName = parseInt(entry.match(/^(\d+)\t/)[1]); + decodeImageAndAttachProc.exec(["bash", "-c", `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'`]); } onExited: (exitCode, exitStatus) => { if (exitCode === 0) { Ai.attachFile(imageDecodeFilePath); } else { - console.error("[AiChat] Failed to decode image in clipboard content") + console.error("[AiChat] Failed to decode image in clipboard content"); } } } @@ -278,37 +275,14 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) ColumnLayout { id: columnLayout - anchors.fill: parent - - RowLayout { // Status - Layout.alignment: Qt.AlignHCenter - spacing: 10 - - StatusItem { - icon: Ai.currentModelHasApiKey ? "key" : "key_off" - statusText: "" - description: Ai.currentModelHasApiKey ? Translation.tr("API key is set\nChange with /key YOUR_API_KEY") : Translation.tr("No API key\nSet it with /key YOUR_API_KEY") - } - StatusSeparator {} - StatusItem { - icon: "device_thermostat" - statusText: Ai.temperature.toFixed(1) - description: Translation.tr("Temperature\nChange with /temp VALUE") - } - StatusSeparator { - visible: Ai.tokenCount.total > 0 - } - StatusItem { - visible: Ai.tokenCount.total > 0 - icon: "token" - statusText: Ai.tokenCount.total - description: Translation.tr("Total token count\nInput: %1\nOutput: %2") - .arg(Ai.tokenCount.input) - .arg(Ai.tokenCount.output) - } + anchors { + fill: parent + margins: root.padding } + spacing: root.padding - Item { // Messages + Item { + // Messages Layout.fillWidth: true Layout.fillHeight: true layer.enabled: true @@ -320,6 +294,55 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) } } + StyledRectangularShadow { + z: 1 + target: statusBg + opacity: messageListView.atYBeginning ? 0 : 1 + visible: opacity > 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + Rectangle { + id: statusBg + z: 2 + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: 4 + } + implicitWidth: statusRowLayout.implicitWidth + 10 * 2 + implicitHeight: Math.max(statusRowLayout.implicitHeight, 38) + radius: Appearance.rounding.normal - root.padding + color: Appearance.colors.colLayer2 + RowLayout { + id: statusRowLayout + anchors.centerIn: parent + spacing: 10 + + StatusItem { + icon: Ai.currentModelHasApiKey ? "key" : "key_off" + statusText: "" + description: Ai.currentModelHasApiKey ? Translation.tr("API key is set\nChange with /key YOUR_API_KEY") : Translation.tr("No API key\nSet it with /key YOUR_API_KEY") + } + StatusSeparator {} + StatusItem { + icon: "device_thermostat" + statusText: Ai.temperature.toFixed(1) + description: Translation.tr("Temperature\nChange with /temp VALUE") + } + StatusSeparator { + visible: Ai.tokenCount.total > 0 + } + StatusItem { + visible: Ai.tokenCount.total > 0 + icon: "token" + statusText: Ai.tokenCount.total + description: Translation.tr("Total token count\nInput: %1\nOutput: %2").arg(Ai.tokenCount.input).arg(Ai.tokenCount.output) + } + } + } + ScrollEdgeFade { z: 1 target: messageListView @@ -332,16 +355,20 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) anchors.fill: parent spacing: 10 popin: false + topMargin: statusBg.implicitHeight + statusBg.anchors.topMargin * 2 touchpadScrollFactor: Config.options.interactions.scrolling.touchpadScrollFactor * 1.4 mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4 property int lastResponseLength: 0 onContentHeightChanged: { - if (atYEnd) Qt.callLater(positionViewAtEnd); + if (atYEnd) + Qt.callLater(positionViewAtEnd); } - onCountChanged: { // Auto-scroll when new messages are added - if (atYEnd) Qt.callLater(positionViewAtEnd); + onCountChanged: { + // Auto-scroll when new messages are added + if (atYEnd) + Qt.callLater(positionViewAtEnd); } add: null // Prevent function calls from being janky @@ -357,7 +384,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) required property int index messageIndex: index messageData: { - Ai.messageByID[modelData] + Ai.messageByID[modelData]; } messageInputField: root.inputField } @@ -393,8 +420,8 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) Repeater { id: suggestionRepeater model: { - suggestions.selectedIndex = 0 - return root.suggestionList.slice(0, 10) + suggestions.selectedIndex = 0; + return root.suggestionList.slice(0, 10); } delegate: ApiCommandButton { id: commandButton @@ -413,7 +440,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) } } onClicked: { - suggestions.acceptSuggestion(modelData.name) + suggestions.acceptSuggestion(modelData.name); } } } @@ -443,14 +470,10 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) id: inputWrapper property real spacing: 5 Layout.fillWidth: true - radius: Appearance.rounding.small - color: Appearance.colors.colLayer1 - implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin - + commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + spacing, 45) - + (attachedFileIndicator.implicitHeight + spacing + attachedFileIndicator.anchors.topMargin) + radius: Appearance.rounding.normal - root.padding + color: Appearance.colors.colLayer2 + implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin + commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + spacing, 45) + (attachedFileIndicator.implicitHeight + spacing + attachedFileIndicator.anchors.topMargin) clip: true - border.color: Appearance.colors.colOutlineVariant - border.width: 1 Behavior on implicitHeight { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) @@ -488,121 +511,122 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) background: null - onTextChanged: { // Handle suggestions + onTextChanged: { + // Handle suggestions if (messageInputField.text.length === 0) { - root.suggestionQuery = "" - root.suggestionList = [] - return + root.suggestionQuery = ""; + root.suggestionList = []; + return; } else if (messageInputField.text.startsWith(`${root.commandPrefix}model`)) { - root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "" + root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""; const modelResults = Fuzzy.go(root.suggestionQuery, Ai.modelList.map(model => { return { name: Fuzzy.prepare(model), - obj: model, - } + obj: model + }; }), { all: true, key: "name" - }) + }); root.suggestionList = modelResults.map(model => { return { name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "model ") : ""}${model.target}`, displayName: `${Ai.models[model.target].name}`, - description: `${Ai.models[model.target].description}`, - } - }) + description: `${Ai.models[model.target].description}` + }; + }); } else if (messageInputField.text.startsWith(`${root.commandPrefix}prompt`)) { - root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "" + root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""; const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.promptFiles.map(file => { return { name: Fuzzy.prepare(file), - obj: file, - } + obj: file + }; }), { all: true, key: "name" - }) + }); root.suggestionList = promptFileResults.map(file => { return { name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "prompt ") : ""}${file.target}`, displayName: `${FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target))}`, - description: Translation.tr("Load prompt from %1").arg(file.target), - } - }) + description: Translation.tr("Load prompt from %1").arg(file.target) + }; + }); } else if (messageInputField.text.startsWith(`${root.commandPrefix}save`)) { - root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "" + root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""; const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => { return { name: Fuzzy.prepare(file), - obj: file, - } + obj: file + }; }), { all: true, key: "name" - }) + }); root.suggestionList = promptFileResults.map(file => { - const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim() + const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim(); return { name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "save ") : ""}${chatName}`, displayName: `${chatName}`, - description: Translation.tr("Save chat to %1").arg(chatName), - } - }) + description: Translation.tr("Save chat to %1").arg(chatName) + }; + }); } else if (messageInputField.text.startsWith(`${root.commandPrefix}load`)) { - root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "" + root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""; const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => { return { name: Fuzzy.prepare(file), - obj: file, - } + obj: file + }; }), { all: true, key: "name" - }) + }); root.suggestionList = promptFileResults.map(file => { - const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim() + const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim(); return { name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "load ") : ""}${chatName}`, displayName: `${chatName}`, - description: Translation.tr(`Load chat from %1`).arg(file.target), - } - }) + description: Translation.tr(`Load chat from %1`).arg(file.target) + }; + }); } else if (messageInputField.text.startsWith(`${root.commandPrefix}tool`)) { - root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "" + root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""; const toolResults = Fuzzy.go(root.suggestionQuery, Ai.availableTools.map(tool => { return { name: Fuzzy.prepare(tool), - obj: tool, - } + obj: tool + }; }), { all: true, key: "name" - }) + }); root.suggestionList = toolResults.map(tool => { - const toolName = tool.target + const toolName = tool.target; return { name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "tool ") : ""}${tool.target}`, displayName: toolName, - description: Ai.toolDescriptions[toolName], - } - }) - } else if(messageInputField.text.startsWith(root.commandPrefix)) { - root.suggestionQuery = messageInputField.text + description: Ai.toolDescriptions[toolName] + }; + }); + } else if (messageInputField.text.startsWith(root.commandPrefix)) { + root.suggestionQuery = messageInputField.text; root.suggestionList = root.allCommands.filter(cmd => cmd.name.startsWith(messageInputField.text.substring(1))).map(cmd => { return { name: `${root.commandPrefix}${cmd.name}`, - description: `${cmd.description}`, - } - }) + description: `${cmd.description}` + }; + }); } } function accept() { - root.handleInput(text) - text = "" + root.handleInput(text); + text = ""; } - Keys.onPressed: (event) => { + Keys.onPressed: event => { if (event.key === Qt.Key_Tab) { suggestions.acceptSelectedWord(); event.accepted = true; @@ -615,35 +639,41 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) } else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) { if (event.modifiers & Qt.ShiftModifier) { // Insert newline - messageInputField.insert(messageInputField.cursorPosition, "\n") - event.accepted = true - } else { // Accept text - const inputText = messageInputField.text - messageInputField.clear() - root.handleInput(inputText) - event.accepted = true + messageInputField.insert(messageInputField.cursorPosition, "\n"); + event.accepted = true; + } else { + // Accept text + const inputText = messageInputField.text; + messageInputField.clear(); + root.handleInput(inputText); + event.accepted = true; } - } else if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_V) { // Intercept Ctrl+V to handle image/file pasting - if (event.modifiers & Qt.ShiftModifier) { // Let Shift+Ctrl+V = plain paste - messageInputField.text += Quickshell.clipboardText + } else if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_V) { + // Intercept Ctrl+V to handle image/file pasting + if (event.modifiers & Qt.ShiftModifier) { + // Let Shift+Ctrl+V = plain paste + messageInputField.text += Quickshell.clipboardText; event.accepted = true; return; } // Try image paste first - const currentClipboardEntry = Cliphist.entries[0] - const cleanCliphistEntry = StringUtils.cleanCliphistEntry(currentClipboardEntry) - if (/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(currentClipboardEntry)) { // First entry = currently copied entry = image? - decodeImageAndAttachProc.handleEntry(currentClipboardEntry) + const currentClipboardEntry = Cliphist.entries[0]; + const cleanCliphistEntry = StringUtils.cleanCliphistEntry(currentClipboardEntry); + if (/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(currentClipboardEntry)) { + // First entry = currently copied entry = image? + decodeImageAndAttachProc.handleEntry(currentClipboardEntry); event.accepted = true; return; - } else if (cleanCliphistEntry.startsWith("file://")) { // First entry = currently copied entry = image? - const fileName = decodeURIComponent(cleanCliphistEntry) + } else if (cleanCliphistEntry.startsWith("file://")) { + // First entry = currently copied entry = image? + const fileName = decodeURIComponent(cleanCliphistEntry); Ai.attachFile(fileName); event.accepted = true; return; } event.accepted = false; // No image, let text pasting proceed - } else if (event.key === Qt.Key_Escape) { // Esc to detach file + } else if (event.key === Qt.Key_Escape) { + // Esc to detach file if (Ai.pendingFilePath.length > 0) { Ai.attachFile(""); event.accepted = true; @@ -668,19 +698,18 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) anchors.fill: parent cursorShape: sendButton.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor onClicked: { - const inputText = messageInputField.text - root.handleInput(inputText) - messageInputField.clear() + const inputText = messageInputField.text; + root.handleInput(inputText); + messageInputField.clear(); } } contentItem: MaterialSymbol { anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter - iconSize: Appearance.font.pixelSize.larger - // fill: sendButton.enabled ? 1 : 0 + iconSize: 22 color: sendButton.enabled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2Disabled - text: "send" + text: "arrow_upward" } } } @@ -699,59 +728,58 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) { name: "", sendDirectly: false, - dontAddSpace: true, - }, + dontAddSpace: true + }, { name: "clear", - sendDirectly: true, - }, + sendDirectly: true + }, ] - ApiInputBoxIndicator { // Model indicator + ApiInputBoxIndicator { + // Model indicator icon: "api" text: Ai.getModel().name - tooltipText: Translation.tr("Current model: %1\nSet it with %2model MODEL") - .arg(Ai.getModel().name) - .arg(root.commandPrefix) + tooltipText: Translation.tr("Current model: %1\nSet it with %2model MODEL").arg(Ai.getModel().name).arg(root.commandPrefix) } - ApiInputBoxIndicator { // Tool indicator + ApiInputBoxIndicator { + // Tool indicator icon: "service_toolbox" text: Ai.currentTool.charAt(0).toUpperCase() + Ai.currentTool.slice(1) - tooltipText: Translation.tr("Current tool: %1\nSet it with %2tool TOOL") - .arg(Ai.currentTool) - .arg(root.commandPrefix) + tooltipText: Translation.tr("Current tool: %1\nSet it with %2tool TOOL").arg(Ai.currentTool).arg(root.commandPrefix) } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } - ButtonGroup { // Command buttons + ButtonGroup { + // Command buttons padding: 0 - Repeater { // Command buttons + Repeater { + // Command buttons model: commandButtonsRow.commandsShown delegate: ApiCommandButton { property string commandRepresentation: `${root.commandPrefix}${modelData.name}` buttonText: commandRepresentation downAction: () => { if (modelData.sendDirectly) { - root.handleInput(commandRepresentation) + root.handleInput(commandRepresentation); } else { - messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " ") - messageInputField.cursorPosition = messageInputField.text.length - messageInputField.forceActiveFocus() + messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " "); + messageInputField.cursorPosition = messageInputField.text.length; + messageInputField.forceActiveFocus(); } if (modelData.name === "clear") { - messageInputField.text = "" + messageInputField.text = ""; } } } } } } - } - } - } diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml index 1e1d483c9..ff4b22d42 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml @@ -12,6 +12,8 @@ import Quickshell Item { id: root + property real padding: 4 + property var inputField: tagInputField readonly property var responses: Booru.responses property string previewDownloadPath: Directories.booruPreviews @@ -141,7 +143,11 @@ Item { ColumnLayout { id: columnLayout - anchors.fill: parent + anchors { + fill: parent + margins: root.padding + } + spacing: root.padding Item { Layout.fillWidth: true @@ -317,14 +323,12 @@ Item { id: tagInputContainer property real columnSpacing: 5 Layout.fillWidth: true - radius: Appearance.rounding.small - color: Appearance.colors.colLayer1 + radius: Appearance.rounding.normal - root.padding + color: Appearance.colors.colLayer2 implicitWidth: tagInputField.implicitWidth implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin + commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + columnSpacing, 45) clip: true - border.color: Appearance.colors.colOutlineVariant - border.width: 1 Behavior on implicitHeight { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) @@ -456,10 +460,9 @@ Item { contentItem: MaterialSymbol { anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter - iconSize: Appearance.font.pixelSize.larger - // fill: sendButton.enabled ? 1 : 0 + iconSize: 22 color: sendButton.enabled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2Disabled - text: "send" + text: "arrow_upward" } } } diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeft.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeft.qml index 6d8009f0b..9a26b07be 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeft.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeft.qml @@ -127,6 +127,7 @@ Scope { // Scope sourceComponent: FloatingWindow { id: detachedSidebarRoot property var contentParent: detachedSidebarBackground + color: "transparent" visible: GlobalStates.sidebarLeftOpen onVisibleChanged: { diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml index ff1c34509..f3ebfd356 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml @@ -21,7 +21,6 @@ Item { ...(root.translatorEnabled ? [{"icon": "translate", "name": Translation.tr("Translator")}] : []), ...((root.animeEnabled && !root.animeCloset) ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : []) ] - property int selectedTab: 0 property int tabCount: swipeView.count function focusActiveItem() { @@ -31,67 +30,64 @@ Item { Keys.onPressed: (event) => { if (event.modifiers === Qt.ControlModifier) { if (event.key === Qt.Key_PageDown) { - root.selectedTab = Math.min(root.selectedTab + 1, root.tabCount - 1) + swipeView.incrementCurrentIndex() event.accepted = true; - } + } else if (event.key === Qt.Key_PageUp) { - root.selectedTab = Math.max(root.selectedTab - 1, 0) - event.accepted = true; - } - else if (event.key === Qt.Key_Tab) { - root.selectedTab = (root.selectedTab + 1) % root.tabCount; - event.accepted = true; - } - else if (event.key === Qt.Key_Backtab) { - root.selectedTab = (root.selectedTab - 1 + root.tabCount) % root.tabCount; + swipeView.decrementCurrentIndex() event.accepted = true; } } } ColumnLayout { - anchors.fill: parent - anchors.margins: sidebarPadding - + anchors { + fill: parent + margins: sidebarPadding + } spacing: sidebarPadding - PrimaryTabBar { // Tab strip - id: tabBar - visible: root.tabButtonList.length > 1 - tabButtonList: root.tabButtonList - Synchronizer on currentIndex { - property alias source: root.selectedTab + Toolbar { + Layout.alignment: Qt.AlignHCenter + enableShadow: false + ToolbarTabBar { + id: tabBar + Layout.alignment: Qt.AlignHCenter + tabButtonList: root.tabButtonList + currentIndex: swipeView.currentIndex } } - SwipeView { // Content pages - id: swipeView - Layout.topMargin: 5 + Rectangle { Layout.fillWidth: true Layout.fillHeight: true - spacing: 10 - - currentIndex: root.selectedTab - onCurrentIndexChanged: { - tabBar.enableIndicatorAnimation = true - root.selectedTab = currentIndex - } + implicitWidth: swipeView.implicitWidth + implicitHeight: swipeView.implicitHeight + radius: Appearance.rounding.normal + color: Appearance.colors.colLayer1 - clip: true - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: swipeView.width - height: swipeView.height - radius: Appearance.rounding.small + SwipeView { // Content pages + id: swipeView + anchors.fill: parent + spacing: 10 + currentIndex: tabBar.currentIndex + + clip: true + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: swipeView.width + height: swipeView.height + radius: Appearance.rounding.small + } } - } - contentChildren: [ - ...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []), - ...(root.translatorEnabled ? [translator.createObject()] : []), - ...(root.animeEnabled ? [anime.createObject()] : []) - ] + contentChildren: [ + ...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []), + ...(root.translatorEnabled ? [translator.createObject()] : []), + ...(root.animeEnabled ? [anime.createObject()] : []) + ] + } } Component { diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/Translator.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/Translator.qml index 41f4fffab..bde48f532 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/Translator.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/Translator.qml @@ -13,17 +13,24 @@ import Quickshell.Io */ Item { id: root + + // Sizes + property real padding: 4 + // Widgets property var inputField: inputCanvas.inputTextArea + // Widget variables property bool translationFor: false // Indicates if the translation is for an autocorrected text property string translatedText: "" property list languages: [] + // Options property string targetLanguage: Config.options.language.translator.targetLanguage property string sourceLanguage: Config.options.language.translator.sourceLanguage property string hostLanguage: targetLanguage + // States property bool showLanguageSelector: false property bool languageSelectorTarget: false // true for target language, false for source language @@ -99,7 +106,11 @@ Item { } ColumnLayout { - anchors.fill: parent + anchors { + fill: parent + margins: root.padding + } + StyledFlickable { Layout.fillWidth: true Layout.fillHeight: true diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/translator/TextCanvas.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/translator/TextCanvas.qml index 7b4841217..b585a07d7 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/translator/TextCanvas.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/translator/TextCanvas.qml @@ -17,10 +17,8 @@ Rectangle { default property alias actionButtons: actions.data Layout.fillWidth: true implicitHeight: Math.max(150, inputColumn.implicitHeight) - color: isInput ? Appearance.colors.colLayer1 : Appearance.colors.colSurfaceContainer + color: Appearance.colors.colLayer2 radius: Appearance.rounding.normal - border.color: isInput ? Appearance.colors.colOutlineVariant : "transparent" - border.width: isInput ? 1 : 0 signal inputTextChanged(); // Signal emitted when text changes diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/nightLight/NightLightDialog.qml b/dots/.config/quickshell/ii/modules/sidebarRight/nightLight/NightLightDialog.qml index 9343c4588..f0286fc38 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/nightLight/NightLightDialog.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/nightLight/NightLightDialog.qml @@ -102,7 +102,7 @@ WindowDialog { right: parent.right } iconSize: Appearance.font.pixelSize.larger - buttonIcon: "destruction" + buttonIcon: "flash_off" text: Translation.tr("Enable") checked: Config.options.light.antiFlashbang.enable onCheckedChanged: { diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml index b5e49d3b0..f8c93bfff 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/AndroidQuickPanel.qml @@ -29,7 +29,7 @@ AbstractQuickPanel { readonly property real baseCellHeight: 56 // Toggles - readonly property list availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile","musicRecognition"] + readonly property list availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile","musicRecognition", "antiFlashbang"] readonly property int columns: Config.options.sidebar.quickToggles.android.columns readonly property list toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : [] readonly property list toggleRows: toggleRowsForList(toggles) diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidAntiFlashbangToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidAntiFlashbangToggle.qml new file mode 100644 index 000000000..4fc8ee4f5 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidAntiFlashbangToggle.qml @@ -0,0 +1,29 @@ +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import Quickshell + +AndroidQuickToggleButton { + id: root + + property bool auto: Config.options.light.night.automatic + + name: Translation.tr("Anti-flashbang") + + toggled: Config.options.light.antiFlashbang.enable + buttonIcon: "flash_off" + + mainAction: () => { + Config.options.light.antiFlashbang.enable = !Config.options.light.antiFlashbang.enable; + } + + altAction: () => { + root.openMenu() + } + + StyledToolTip { + text: Translation.tr("Anti-flashbang") + } +} + diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidAudioToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidAudioToggle.qml index 3fff4d620..52f7459a7 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidAudioToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidAudioToggle.qml @@ -12,7 +12,7 @@ AndroidQuickToggleButton { statusText: toggled ? Translation.tr("Unmuted") : Translation.tr("Muted") toggled: !Audio.sink?.audio?.muted buttonIcon: Audio.sink?.audio?.muted ? "volume_off" : "volume_up" - onClicked: { + mainAction: () => { Audio.sink.audio.muted = !Audio.sink.audio.muted } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidBluetoothToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidBluetoothToggle.qml index 3b4f8fa8d..ae76d5751 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidBluetoothToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidBluetoothToggle.qml @@ -14,7 +14,7 @@ AndroidQuickToggleButton { toggled: BluetoothStatus.enabled buttonIcon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled" - onClicked: { + mainAction: () => { Bluetooth.defaultAdapter.enabled = !Bluetooth.defaultAdapter?.enabled } altAction: () => { diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidCloudflareWarpToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidCloudflareWarpToggle.qml index d703c67dd..5b65e673d 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidCloudflareWarpToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidCloudflareWarpToggle.qml @@ -13,7 +13,7 @@ AndroidQuickToggleButton { toggled: false buttonIcon: "cloud_lock" - onClicked: { + mainAction: () => { if (toggled) { root.toggled = false Quickshell.execDetached(["warp-cli", "disconnect"]) diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidColorPickerToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidColorPickerToggle.qml index 82efbf544..8fdc59205 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidColorPickerToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidColorPickerToggle.qml @@ -13,7 +13,7 @@ AndroidQuickToggleButton { toggled: false buttonIcon: "colorize" - onClicked: { + mainAction: () => { GlobalStates.sidebarRightOpen = false; delayedActionTimer.start() } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidDarkModeToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidDarkModeToggle.qml index 668f840f2..11be0dae6 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidDarkModeToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidDarkModeToggle.qml @@ -13,7 +13,7 @@ AndroidQuickToggleButton { toggled: Appearance.m3colors.darkmode buttonIcon: "contrast" - onClicked: event => { + mainAction: () => { if (Appearance.m3colors.darkmode) { Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]); } else { diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml index c907abf77..bfd14335c 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidEasyEffectsToggle.qml @@ -16,7 +16,7 @@ AndroidQuickToggleButton { EasyEffects.fetchActiveState() } - onClicked: { + mainAction: () => { EasyEffects.toggle() } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidGameModeToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidGameModeToggle.qml index d4ab4fd81..ae63622fe 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidGameModeToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidGameModeToggle.qml @@ -13,7 +13,7 @@ AndroidQuickToggleButton { toggled: toggled buttonIcon: "gamepad" - onClicked: { + mainAction: () => { root.toggled = !root.toggled if (root.toggled) { Quickshell.execDetached(["bash", "-c", `hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`]) diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidIdleInhibitorToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidIdleInhibitorToggle.qml index 17dedd7df..2daf1f914 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidIdleInhibitorToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidIdleInhibitorToggle.qml @@ -11,7 +11,7 @@ AndroidQuickToggleButton { toggled: Idle.inhibit buttonIcon: "coffee" - onClicked: { + mainAction: () => { Idle.toggleInhibit() } StyledToolTip { diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMicToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMicToggle.qml index 40f06f0a4..7ffeea7f7 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMicToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMicToggle.qml @@ -12,7 +12,7 @@ AndroidQuickToggleButton { statusText: toggled ? Translation.tr("Enabled") : Translation.tr("Muted") toggled: !Audio.source?.audio?.muted buttonIcon: Audio.source?.audio?.muted ? "mic_off" : "mic" - onClicked: { + mainAction: () => { Audio.source.audio.muted = !Audio.source.audio.muted } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml index e8f17f657..23e2cda92 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidMusicRecognition.qml @@ -21,7 +21,7 @@ AndroidQuickToggleButton { text: Translation.tr("Recognize music | Right-click to toggle source") } - onClicked: { + mainAction: () => { SongRec.toggleRunning() } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNetworkToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNetworkToggle.qml index 53576febb..eaf24e141 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNetworkToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNetworkToggle.qml @@ -12,7 +12,7 @@ AndroidQuickToggleButton { toggled: Network.wifiStatus !== "disabled" buttonIcon: Network.materialSymbol - onClicked: Network.toggleWifi() + mainAction: () => Network.toggleWifi() altAction: () => { root.openMenu() } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNightLightToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNightLightToggle.qml index 699d93dc3..133b0ffcd 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNightLightToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNightLightToggle.qml @@ -14,7 +14,8 @@ AndroidQuickToggleButton { toggled: Hyprsunset.active buttonIcon: auto ? "night_sight_auto" : "bedtime" - onClicked: { + + mainAction: () => { Hyprsunset.toggle() } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNotificationToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNotificationToggle.qml index dc1f381cd..eda39d716 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNotificationToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidNotificationToggle.qml @@ -13,7 +13,7 @@ AndroidQuickToggleButton { toggled: !Notifications.silent buttonIcon: toggled ? "notifications_active" : "notifications_paused" - onClicked: { + mainAction: () => { Notifications.silent = !Notifications.silent; } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidOnScreenKeyboardToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidOnScreenKeyboardToggle.qml index 7a4283689..779917802 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidOnScreenKeyboardToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidOnScreenKeyboardToggle.qml @@ -11,7 +11,8 @@ AndroidQuickToggleButton { name: Translation.tr("Virtual Keyboard") toggled: GlobalStates.oskOpen buttonIcon: toggled ? "keyboard_hide" : "keyboard" - onClicked: { + + mainAction: () => { GlobalStates.oskOpen = !GlobalStates.oskOpen } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidPowerProfileToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidPowerProfileToggle.qml index 064180ae5..381113308 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidPowerProfileToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidPowerProfileToggle.qml @@ -22,7 +22,7 @@ AndroidQuickToggleButton { case PowerProfile.Performance: return "Performance" } - onClicked: (event) => { + mainAction: () => { if (PowerProfiles.hasPerformanceProfile) { switch(PowerProfiles.profile) { case PowerProfile.PowerSaver: PowerProfiles.profile = PowerProfile.Balanced diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidQuickToggleButton.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidQuickToggleButton.qml index c2658cede..96401a6c0 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidQuickToggleButton.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidQuickToggleButton.qml @@ -13,6 +13,7 @@ GroupButton { required property bool expandedSize required property string buttonIcon required property string name + required property var mainAction property string statusText: toggled ? Translation.tr("Active") : Translation.tr("Inactive") required property real baseCellWidth @@ -54,6 +55,11 @@ GroupButton { property color colText: (toggled && !(altAction && expandedSize)) ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer2 property color colIcon: expandedSize ? (root.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer3) : colText + onClicked: { + if (root.expandedSize && root.altAction) root.altAction(); + else root.mainAction(); + } + contentItem: RowLayout { id: contentItem spacing: 4 @@ -64,35 +70,63 @@ GroupButton { rightMargin: root.horizontalPadding } - Rectangle { + // Icon + MouseArea { + id: iconMouseArea + hoverEnabled: true + acceptedButtons: (root.expandedSize && root.altAction) ? Qt.LeftButton : Qt.NoButton Layout.alignment: Qt.AlignHCenter Layout.fillHeight: true Layout.topMargin: root.verticalPadding Layout.bottomMargin: root.verticalPadding - implicitWidth: height - radius: root.radius - root.verticalPadding - color: { - const baseColor = root.toggled ? Appearance.colors.colPrimary : Appearance.colors.colLayer3 - const transparentizeAmount = (root.altAction && root.expandedSize) ? 0 : 1 - return ColorUtils.transparentize(baseColor, transparentizeAmount) - } + implicitHeight: iconBackground.implicitHeight + implicitWidth: iconBackground.implicitWidth + cursorShape: Qt.PointingHandCursor - Behavior on radius { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - Behavior on color { - animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) - } + onClicked: root.mainAction() - MaterialSymbol { - anchors.centerIn: parent - fill: root.toggled ? 1 : 0 - iconSize: root.expandedSize ? 22 : 24 - color: root.colIcon - text: root.buttonIcon + Rectangle { + id: iconBackground + anchors.fill: parent + implicitWidth: height + radius: root.radius - root.verticalPadding + color: { + const baseColor = root.toggled ? Appearance.colors.colPrimary : Appearance.colors.colLayer3 + const transparentizeAmount = (root.altAction && root.expandedSize) ? 0 : 1 + return ColorUtils.transparentize(baseColor, transparentizeAmount) + } + + Behavior on radius { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + + MaterialSymbol { + anchors.centerIn: parent + fill: root.toggled ? 1 : 0 + iconSize: root.expandedSize ? 22 : 24 + color: root.colIcon + text: root.buttonIcon + } + + // State layer + Loader { + anchors.fill: parent + active: (root.expandedSize && root.altAction) + sourceComponent: Rectangle { + radius: iconBackground.radius + color: ColorUtils.transparentize(root.colIcon, iconMouseArea.containsPress ? 0.88 : iconMouseArea.containsMouse ? 0.95 : 1) + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + } + } } } + // Text column for expanded size Loader { Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true @@ -119,8 +153,9 @@ GroupButton { } font { pixelSize: Appearance.font.pixelSize.smaller + weight: 100 } - color: Appearance.colors.colSubtext + color: root.colText elide: Text.ElideRight text: root.statusText } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidScreenSnipToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidScreenSnipToggle.qml index 5a22b616a..c4b4a5479 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidScreenSnipToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidScreenSnipToggle.qml @@ -14,7 +14,7 @@ AndroidQuickToggleButton { toggled: false buttonIcon: "screenshot_region" - onClicked: { + mainAction: () => { GlobalStates.sidebarRightOpen = false; delayedActionTimer.start() } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml index aa5356e2e..43cc712b5 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml @@ -245,4 +245,20 @@ DelegateChooser { cellSize: modelData.size } } + DelegateChoice { roleValue: "antiFlashbang"; AndroidAntiFlashbangToggle { + required property int index + required property var modelData + buttonIndex: root.startingIndex + index + buttonData: modelData + editMode: root.editMode + expandedSize: modelData.size > 1 + baseCellWidth: root.baseCellWidth + baseCellHeight: root.baseCellHeight + cellSpacing: root.spacing + cellSize: modelData.size + onOpenMenu: { + root.openNightLightDialog() + } + } } + } diff --git a/dots/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml b/dots/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml index a093d38ca..c237a8854 100644 --- a/dots/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml +++ b/dots/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml @@ -186,6 +186,8 @@ MouseArea { colBackgroundToggled: Appearance.colors.colSecondaryContainer colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover colRippleToggled: Appearance.colors.colSecondaryContainerActive + buttonRadius: height / 2 + implicitHeight: 38 contentItem: RowLayout { MaterialSymbol { diff --git a/dots/.config/quickshell/ii/scripts/colors/scheme_for_image.py b/dots/.config/quickshell/ii/scripts/colors/scheme_for_image.py index 8aa0ccbea..7adc8ce70 100755 --- a/dots/.config/quickshell/ii/scripts/colors/scheme_for_image.py +++ b/dots/.config/quickshell/ii/scripts/colors/scheme_for_image.py @@ -30,12 +30,7 @@ def image_colorfulness(image): # scheme-content respects the image's colors very well, but it might # look too saturated, so we only use it for not very colorful images to be safe def pick_scheme(colorfulness): - if colorfulness < 10: - # return "scheme-monochrome" - return "scheme-content" - elif colorfulness < 20: - return "scheme-content" - elif colorfulness < 50: + if colorfulness < 40: return "scheme-neutral" else: return "scheme-tonal-spot" diff --git a/dots/.config/quickshell/ii/scripts/keyring/is_unlocked.sh b/dots/.config/quickshell/ii/scripts/keyring/is_unlocked.sh new file mode 100755 index 000000000..d4063d816 --- /dev/null +++ b/dots/.config/quickshell/ii/scripts/keyring/is_unlocked.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +locked_state=$(busctl --user get-property org.freedesktop.secrets \ + /org/freedesktop/secrets/collection/login \ + org.freedesktop.Secret.Collection Locked) +if [[ "${locked_state}" == "b false" ]]; then + echo 'Keyring is unlocked' >&2 + exit 0 +else + echo 'Keyring is locked' >&2 + exit 1 +fi diff --git a/dots/.config/quickshell/ii/scripts/keyring/try_lookup.sh b/dots/.config/quickshell/ii/scripts/keyring/try_lookup.sh new file mode 100755 index 000000000..a076aac91 --- /dev/null +++ b/dots/.config/quickshell/ii/scripts/keyring/try_lookup.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +data=$(secret-tool lookup 'application' 'illogical-impulse') +if [[ -z "$data" ]]; then + if "${SCRIPT_DIR}/is_unlocked.sh"; then + echo 'not found' + exit 1 + else + echo 'locked' + exit 2 + fi +fi +echo "$data" diff --git a/dots/.config/quickshell/ii/scripts/keyring/unlock.sh b/dots/.config/quickshell/ii/scripts/keyring/unlock.sh index b255f8e0f..30509aa37 100755 --- a/dots/.config/quickshell/ii/scripts/keyring/unlock.sh +++ b/dots/.config/quickshell/ii/scripts/keyring/unlock.sh @@ -1,12 +1,10 @@ #!/usr/bin/env bash # Based on https://unix.stackexchange.com/a/602935 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + # Skip if already unlocked -locked_state=$(busctl --user get-property org.freedesktop.secrets \ - /org/freedesktop/secrets/collection/login \ - org.freedesktop.Secret.Collection Locked) -if [[ "${locked_state}" == "b false" ]]; then - echo 'Keyring is already unlocked.' >&2 +if "${SCRIPT_DIR}/is_unlocked.sh"; then exit 1 fi diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index 47b1d151f..ccd237de8 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -532,8 +532,6 @@ Singleton { modelId = modelId.toLowerCase() if (modelList.indexOf(modelId) !== -1) { const model = models[modelId] - // Fetch API keys if needed - if (model?.requires_key) KeyringStorage.fetchKeyringData(); // See if policy prevents online models if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) { root.addMessage( @@ -641,6 +639,10 @@ Singleton { function makeRequest() { const model = models[currentModelId]; + + // Fetch API keys if needed + if (model?.requires_key && !KeyringStorage.loaded) KeyringStorage.fetchKeyringData(); + requester.currentStrategy = root.currentApiStrategy; requester.currentStrategy.reset(); // Reset strategy state diff --git a/dots/.config/quickshell/ii/services/AppSearch.qml b/dots/.config/quickshell/ii/services/AppSearch.qml index 6be896404..196e1bed3 100644 --- a/dots/.config/quickshell/ii/services/AppSearch.qml +++ b/dots/.config/quickshell/ii/services/AppSearch.qml @@ -86,6 +86,10 @@ Singleton { return str.toLowerCase().replace(/\s+/g, "-"); } + function getUndescoreToKebabAppName(str) { + return str.toLowerCase().replace(/_/g, "-"); + } + function guessIcon(str) { if (!str || str.length == 0) return "image-missing"; @@ -124,6 +128,8 @@ Singleton { const kebabNormalizedGuess = getKebabNormalizedAppName(str); if (iconExists(kebabNormalizedGuess)) return kebabNormalizedGuess; + const undescoreToKebabGuess = getUndescoreToKebabAppName(str); + if (iconExists(undescoreToKebabGuess)) return undescoreToKebabGuess; // Search in desktop entries const iconSearchResults = Fuzzy.go(str, preppedIcons, { diff --git a/dots/.config/quickshell/ii/services/Booru.qml b/dots/.config/quickshell/ii/services/Booru.qml index 3a13a9bd8..090476b9f 100644 --- a/dots/.config/quickshell/ii/services/Booru.qml +++ b/dots/.config/quickshell/ii/services/Booru.qml @@ -47,7 +47,7 @@ Singleton { } }) }, - "tagSearchTemplate": "https://yande.re/tag.json?order=count&name={{query}}*", + "tagSearchTemplate": "https://yande.re/tag.json?order=count&limit=10&name={{query}}*", "tagMapFunc": (response) => { return response.map(item => { return { @@ -81,7 +81,7 @@ Singleton { } }) }, - "tagSearchTemplate": "https://konachan.net/tag.json?order=count&name={{query}}*", + "tagSearchTemplate": "https://konachan.net/tag.json?order=count&limit=10&name={{query}}*", "tagMapFunc": (response) => { return response.map(item => { return { @@ -142,7 +142,7 @@ Singleton { } }) }, - "tagSearchTemplate": "https://danbooru.donmai.us/tags.json?search[name_matches]={{query}}*", + "tagSearchTemplate": "https://danbooru.donmai.us/tags.json?limit=10&search[name_matches]={{query}}*", "tagMapFunc": (response) => { return response.map(item => { return { @@ -151,7 +151,6 @@ Singleton { } }) } - }, "gelbooru": { "name": "Gelbooru", @@ -178,7 +177,7 @@ Singleton { } }) }, - "tagSearchTemplate": "https://gelbooru.com/index.php?page=dapi&s=tag&q=index&json=1&orderby=count&name_pattern={{query}}%", + "tagSearchTemplate": "https://gelbooru.com/index.php?page=dapi&s=tag&q=index&json=1&orderby=count&limit=10&name_pattern={{query}}%", "tagMapFunc": (response) => { return response.tag.map(item => { return { diff --git a/dots/.config/quickshell/ii/services/Brightness.qml b/dots/.config/quickshell/ii/services/Brightness.qml index 5232f76f9..f3cec016c 100644 --- a/dots/.config/quickshell/ii/services/Brightness.qml +++ b/dots/.config/quickshell/ii/services/Brightness.qml @@ -86,7 +86,7 @@ Singleton { property int rawMaxBrightness: 100 property real brightness property real brightnessMultiplier: 1.0 - property real multipliedBrightness: Math.max(0, Math.min(1, brightness * brightnessMultiplier)) + property real multipliedBrightness: Math.max(0, Math.min(1, brightness * (Config.options.light.antiFlashbang.enable ? brightnessMultiplier : 1))) property bool ready: false property bool animateChanges: !monitor.isDdc diff --git a/dots/.config/quickshell/ii/services/KeyringStorage.qml b/dots/.config/quickshell/ii/services/KeyringStorage.qml index 508247966..1e8562cac 100644 --- a/dots/.config/quickshell/ii/services/KeyringStorage.qml +++ b/dots/.config/quickshell/ii/services/KeyringStorage.qml @@ -1,6 +1,7 @@ pragma Singleton pragma ComponentBehavior: Bound +import qs import qs.modules.common import qs.modules.common.functions import Quickshell; @@ -89,11 +90,13 @@ Singleton { Process { id: getData command: [ // We need to use echo for a newline so splitparser does parse - "bash", "-c", `echo $(secret-tool lookup 'application' 'illogical-impulse')`, + "bash", "-c", `${Directories.scriptPath}/keyring/try_lookup.sh 2> /dev/null`, ] - stdout: SplitParser { - onRead: data => { - if(data.length === 0) return; + stdout: StdioCollector { + id: keyringDataOutputCollector + onStreamFinished: { + const data = keyringDataOutputCollector.text; + if (data.length === 0 || !data.startsWith("{")) return; try { root.keyringData = JSON.parse(data); // console.log("[KeyringStorage] Keyring data fetched:", JSON.stringify(root.keyringData)); @@ -105,13 +108,15 @@ Singleton { } } onExited: (exitCode, exitStatus) => { - // console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode); - if (exitCode !== 0) { - console.error("[KeyringStorage] Failed to get keyring data, reinitializing."); + console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode); + if (exitCode === 1) { + console.error("[KeyringStorage] Entry not found, initializing."); root.keyringData = {}; saveKeyringData() } - root.loaded = true; + if (exitCode !== 2) { + root.loaded = true; + } } } diff --git a/sdata/dist-arch/illogical-impulse-audio/PKGBUILD b/sdata/dist-arch/illogical-impulse-audio/PKGBUILD index 4a9294122..fe645cf37 100644 --- a/sdata/dist-arch/illogical-impulse-audio/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-audio/PKGBUILD @@ -5,11 +5,11 @@ pkgdesc='Illogical Impulse Audio Dependencies' arch=(any) license=(None) depends=( - cava - pavucontrol-qt - wireplumber - pipewire-pulse - libdbusmenu-gtk3 - playerctl + cava + pavucontrol-qt + wireplumber + pipewire-pulse + libdbusmenu-gtk3 + playerctl ) diff --git a/sdata/dist-arch/illogical-impulse-backlight/PKGBUILD b/sdata/dist-arch/illogical-impulse-backlight/PKGBUILD index 451c7e5d5..2cca7c7fa 100644 --- a/sdata/dist-arch/illogical-impulse-backlight/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-backlight/PKGBUILD @@ -5,8 +5,8 @@ pkgdesc='Illogical Impulse Backlight Dependencies' arch=(any) license=(None) depends=( - geoclue - brightnessctl - ddcutil + geoclue + brightnessctl + ddcutil ) diff --git a/sdata/dist-arch/illogical-impulse-basic/PKGBUILD b/sdata/dist-arch/illogical-impulse-basic/PKGBUILD index a403480e8..3948d41bf 100644 --- a/sdata/dist-arch/illogical-impulse-basic/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-basic/PKGBUILD @@ -5,7 +5,6 @@ pkgdesc='Illogical Impulse Basic Dependencies' arch=(any) license=(None) depends=( - axel bc coreutils cliphist @@ -14,7 +13,6 @@ depends=( wget ripgrep jq - meson xdg-user-dirs # Used in install script rsync diff --git a/sdata/dist-arch/illogical-impulse-hyprland/PKGBUILD b/sdata/dist-arch/illogical-impulse-hyprland/PKGBUILD index 797d2d470..8722c9246 100644 --- a/sdata/dist-arch/illogical-impulse-hyprland/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-hyprland/PKGBUILD @@ -6,16 +6,10 @@ arch=(any) license=(None) depends=( hypridle - hyprcursor hyprland - hyprland-qtutils - hyprland-qt-support - hyprlang hyprlock hyprpicker hyprsunset - hyprutils - hyprwayland-scanner xdg-desktop-portal-hyprland wl-clipboard ) diff --git a/sdata/dist-arch/illogical-impulse-kde/PKGBUILD b/sdata/dist-arch/illogical-impulse-kde/PKGBUILD index 99eec5651..0e09ee748 100644 --- a/sdata/dist-arch/illogical-impulse-kde/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-kde/PKGBUILD @@ -5,11 +5,11 @@ pkgdesc='Illogical Impulse KDE Dependencies' arch=(any) license=(None) depends=( - bluedevil - gnome-keyring - networkmanager - plasma-nm - polkit-kde-agent - dolphin - systemsettings + bluedevil + gnome-keyring + networkmanager + plasma-nm + polkit-kde-agent + dolphin + systemsettings ) diff --git a/sdata/dist-arch/illogical-impulse-microtex-git/PKGBUILD b/sdata/dist-arch/illogical-impulse-microtex-git/PKGBUILD index fcb14b68b..b2f48a5bd 100644 --- a/sdata/dist-arch/illogical-impulse-microtex-git/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-microtex-git/PKGBUILD @@ -8,10 +8,10 @@ arch=("x86_64") url="https://github.com/NanoMichael/${_pkgname}" license=('MIT') depends=( - tinyxml2 - gtkmm3 - gtksourceviewmm - cairomm + tinyxml2 + gtkmm3 + gtksourceviewmm + cairomm ) makedepends=("git" "cmake") source=("git+${url}.git") @@ -23,9 +23,9 @@ pkgver() { } prepare() { - cd $_pkgname - sed -i 's/gtksourceviewmm-3.0/gtksourceviewmm-4.0/' CMakeLists.txt - sed -i 's/tinyxml2.so.10/tinyxml2.so.11/' CMakeLists.txt + cd $_pkgname + sed -i 's/gtksourceviewmm-3.0/gtksourceviewmm-4.0/' CMakeLists.txt + sed -i 's/tinyxml2.so.10/tinyxml2.so.11/' CMakeLists.txt } build() { diff --git a/sdata/dist-arch/illogical-impulse-portal/PKGBUILD b/sdata/dist-arch/illogical-impulse-portal/PKGBUILD index 4394d8484..5e01482b7 100644 --- a/sdata/dist-arch/illogical-impulse-portal/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-portal/PKGBUILD @@ -5,9 +5,9 @@ pkgdesc='Illogical Impulse XDG Desktop Portals' arch=(any) license=(None) depends=( - xdg-desktop-portal - xdg-desktop-portal-kde - xdg-desktop-portal-gtk - xdg-desktop-portal-hyprland + xdg-desktop-portal + xdg-desktop-portal-kde + xdg-desktop-portal-gtk + xdg-desktop-portal-hyprland ) diff --git a/sdata/dist-arch/illogical-impulse-python/PKGBUILD b/sdata/dist-arch/illogical-impulse-python/PKGBUILD index fc1ac1e05..a0de2df19 100644 --- a/sdata/dist-arch/illogical-impulse-python/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-python/PKGBUILD @@ -12,5 +12,4 @@ depends=( libsoup3 libportal-gtk4 gobject-introspection - sassc ) diff --git a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD index 96865a0dd..9aac9658c 100644 --- a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD @@ -44,7 +44,7 @@ _pkgsrc="$_pkgname" source=("$_pkgsrc::git+$url.git#commit=$_commit" quickshell-check.hook) sha256sums=('SKIP' - '8543e21aeaaa5441b73a679160e7601a957f16c433e8d6bd9257e80bd0e94083') + '8543e21aeaaa5441b73a679160e7601a957f16c433e8d6bd9257e80bd0e94083') pkgver() { diff --git a/sdata/dist-arch/illogical-impulse-screencapture/PKGBUILD b/sdata/dist-arch/illogical-impulse-screencapture/PKGBUILD index cde216bf0..6373dcb5c 100644 --- a/sdata/dist-arch/illogical-impulse-screencapture/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-screencapture/PKGBUILD @@ -5,10 +5,10 @@ pkgdesc='Illogical Impulse Screenshot and Recording Dependencies' arch=(any) license=(None) depends=( - hyprshot - slurp - swappy - tesseract - tesseract-data-eng - wf-recorder + hyprshot + slurp + swappy + tesseract + tesseract-data-eng + wf-recorder ) diff --git a/sdata/dist-arch/illogical-impulse-toolkit/PKGBUILD b/sdata/dist-arch/illogical-impulse-toolkit/PKGBUILD index 636709f29..f3e4f943b 100644 --- a/sdata/dist-arch/illogical-impulse-toolkit/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-toolkit/PKGBUILD @@ -5,23 +5,23 @@ pkgdesc='Illogical Impulse GTK/Qt Dependencies' arch=(any) license=(None) depends=( - kdialog - qt6-5compat - qt6-avif-image-plugin - qt6-base - qt6-declarative - qt6-imageformats - qt6-multimedia - qt6-positioning - qt6-quicktimeline - qt6-sensors - qt6-svg - qt6-tools - qt6-translations - qt6-virtualkeyboard - qt6-wayland - syntax-highlighting - upower - wtype - ydotool + kdialog + syntax-highlighting + upower + wtype + ydotool + qt6-5compat + qt6-avif-image-plugin + qt6-base + qt6-declarative + qt6-imageformats + qt6-multimedia + qt6-positioning + qt6-quicktimeline + qt6-sensors + qt6-svg + qt6-tools + qt6-translations + qt6-virtualkeyboard + qt6-wayland ) diff --git a/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD b/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD index 45e8bb1d4..76f71d43f 100644 --- a/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD @@ -6,13 +6,11 @@ arch=(any) license=(None) depends=( fuzzel - glib2 # for `gsettings` it seems? + glib2 imagemagick hypridle - hyprutils hyprlock hyprpicker - nm-connection-editor songrec translate-shell wlogout diff --git a/sdata/dist-arch/previous_dependencies.conf b/sdata/dist-arch/previous_dependencies.conf index 4f32959e3..733e63359 100644 --- a/sdata/dist-arch/previous_dependencies.conf +++ b/sdata/dist-arch/previous_dependencies.conf @@ -2,7 +2,6 @@ ### Must be one package per line as it needs to be compared against the explicitly installed list from pacman illogical-impulse-ags archlinux-xdg-menu -axel bc coreutils cliphist @@ -14,7 +13,6 @@ wget ripgrep gojq npm -meson typescript gjs xdg-user-dirs diff --git a/sdata/dist-nix/README.md b/sdata/dist-nix/README.md index 20f80025d..5febf4698 100644 --- a/sdata/dist-nix/README.md +++ b/sdata/dist-nix/README.md @@ -9,7 +9,12 @@ Note that this script must be idempotent. TODO: -- [ ] Write a proper `flake.nix` and `home.nix` and other files under `dist-nix/home-manager/` to install all dependencies that `dist-arch/` does. (**excluding** the screenlock) +- [ ] Fix all TODOs inside `dist-nix`. +- [ ] Warn user if inode-limited filesystem (typically ext4) is used. +- [ ] Deal with error when running `systemctl --user enable ydotool --now`: + ```plain + Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=@.host --user to connect to bus of other user) + ``` ## Attentions ### PAM diff --git a/sdata/dist-nix/home-manager/flake.lock b/sdata/dist-nix/home-manager/flake.lock index 3c2f0f3f0..c184eee32 100644 --- a/sdata/dist-nix/home-manager/flake.lock +++ b/sdata/dist-nix/home-manager/flake.lock @@ -1,57 +1,8 @@ { "nodes": { - "aquamarine": { - "inputs": { - "hyprutils": [ - "hyprland", - "hyprutils" - ], - "hyprwayland-scanner": [ - "hyprland", - "hyprwayland-scanner" - ], - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1760101617, - "narHash": "sha256-8jf/3ZCi+B7zYpIyV04+3wm72BD7Z801IlOzsOACR7I=", - "owner": "hyprwm", - "repo": "aquamarine", - "rev": "1826a9923881320306231b1c2090379ebf9fa4f8", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "aquamarine", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1747046372, - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, "flake-utils": { "inputs": { - "systems": "systems_2" + "systems": "systems" }, "locked": { "lastModified": 1731533236, @@ -67,28 +18,6 @@ "type": "github" } }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "hyprland", - "pre-commit-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, "home-manager": { "inputs": { "nixpkgs": [ @@ -110,269 +39,10 @@ "type": "github" } }, - "hyprcursor": { - "inputs": { - "hyprlang": [ - "hyprland", - "hyprlang" - ], - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1753964049, - "narHash": "sha256-lIqabfBY7z/OANxHoPeIrDJrFyYy9jAM4GQLzZ2feCM=", - "owner": "hyprwm", - "repo": "hyprcursor", - "rev": "44e91d467bdad8dcf8bbd2ac7cf49972540980a5", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprcursor", - "type": "github" - } - }, - "hyprgraphics": { - "inputs": { - "hyprutils": [ - "hyprland", - "hyprutils" - ], - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1760445448, - "narHash": "sha256-fXGjL6dw31FPFRrmIemzGiNSlfvEJTJNsmadZi+qNhI=", - "owner": "hyprwm", - "repo": "hyprgraphics", - "rev": "50fb9f069219f338a11cf0bcccb9e58357d67757", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprgraphics", - "type": "github" - } - }, - "hyprland": { - "inputs": { - "aquamarine": "aquamarine", - "hyprcursor": "hyprcursor", - "hyprgraphics": "hyprgraphics", - "hyprland-protocols": "hyprland-protocols", - "hyprland-qtutils": "hyprland-qtutils", - "hyprlang": "hyprlang", - "hyprutils": "hyprutils", - "hyprwayland-scanner": "hyprwayland-scanner", - "nixpkgs": "nixpkgs", - "pre-commit-hooks": "pre-commit-hooks", - "systems": "systems", - "xdph": "xdph" - }, - "locked": { - "lastModified": 1761780088, - "narHash": "sha256-ylKrWQeIAGyysfHbgZpcWUs9UsbiOBIVXTPqaiV3lf0=", - "owner": "hyprwm", - "repo": "Hyprland", - "rev": "6ade4d58cab67e18aa758ef664e36421cab4d8b2", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "Hyprland", - "type": "github" - } - }, - "hyprland-protocols": { - "inputs": { - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1759610243, - "narHash": "sha256-+KEVnKBe8wz+a6dTLq8YDcF3UrhQElwsYJaVaHXJtoI=", - "owner": "hyprwm", - "repo": "hyprland-protocols", - "rev": "bd153e76f751f150a09328dbdeb5e4fab9d23622", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprland-protocols", - "type": "github" - } - }, - "hyprland-qt-support": { - "inputs": { - "hyprlang": [ - "hyprland", - "hyprland-qtutils", - "hyprlang" - ], - "nixpkgs": [ - "hyprland", - "hyprland-qtutils", - "nixpkgs" - ], - "systems": [ - "hyprland", - "hyprland-qtutils", - "systems" - ] - }, - "locked": { - "lastModified": 1749154592, - "narHash": "sha256-DO7z5CeT/ddSGDEnK9mAXm1qlGL47L3VAHLlLXoCjhE=", - "owner": "hyprwm", - "repo": "hyprland-qt-support", - "rev": "4c8053c3c888138a30c3a6c45c2e45f5484f2074", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprland-qt-support", - "type": "github" - } - }, - "hyprland-qtutils": { - "inputs": { - "hyprland-qt-support": "hyprland-qt-support", - "hyprlang": [ - "hyprland", - "hyprlang" - ], - "hyprutils": [ - "hyprland", - "hyprland-qtutils", - "hyprlang", - "hyprutils" - ], - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1759080228, - "narHash": "sha256-RgDoAja0T1hnF0pTc56xPfLfFOO8Utol2iITwYbUhTk=", - "owner": "hyprwm", - "repo": "hyprland-qtutils", - "rev": "629b15c19fa4082e4ce6be09fdb89e8c3312aed7", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprland-qtutils", - "type": "github" - } - }, - "hyprlang": { - "inputs": { - "hyprutils": [ - "hyprland", - "hyprutils" - ], - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1758927902, - "narHash": "sha256-LZgMds7M94+vuMql2bERQ6LiFFdhgsEFezE4Vn+Ys3A=", - "owner": "hyprwm", - "repo": "hyprlang", - "rev": "4dafa28d4f79877d67a7d1a654cddccf8ebf15da", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprlang", - "type": "github" - } - }, - "hyprutils": { - "inputs": { - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1759619523, - "narHash": "sha256-r1ed7AR2ZEb2U8gy321/Xcp1ho2tzn+gG1te/Wxsj1A=", - "owner": "hyprwm", - "repo": "hyprutils", - "rev": "3df7bde01efb3a3e8e678d1155f2aa3f19e177ef", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprutils", - "type": "github" - } - }, - "hyprwayland-scanner": { - "inputs": { - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1755184602, - "narHash": "sha256-RCBQN8xuADB0LEgaKbfRqwm6CdyopE1xIEhNc67FAbw=", - "owner": "hyprwm", - "repo": "hyprwayland-scanner", - "rev": "b3b0f1f40ae09d4447c20608e5a4faf8bf3c492d", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprwayland-scanner", - "type": "github" - } - }, "nixgl": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs" }, "locked": { "lastModified": 1752054764, @@ -389,22 +59,6 @@ } }, "nixpkgs": { - "locked": { - "lastModified": 1761114652, - "narHash": "sha256-f/QCJM/YhrV/lavyCVz8iU3rlZun6d+dAiC3H+CDle4=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "01f116e4df6a15f4ccdffb1bcd41096869fb385c", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { "locked": { "lastModified": 1746378225, "narHash": "sha256-OeRSuL8PUjIfL3Q0fTbNJD/fmv1R+K2JAOqWJd3Oceg=", @@ -419,7 +73,7 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_2": { "locked": { "lastModified": 1761597516, "narHash": "sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8+ON/0Yy8+a5vsDU=", @@ -434,53 +88,36 @@ "type": "indirect" } }, - "pre-commit-hooks": { + "quickshell": { "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", "nixpkgs": [ - "hyprland", "nixpkgs" ] }, "locked": { - "lastModified": 1760663237, - "narHash": "sha256-BflA6U4AM1bzuRMR8QqzPXqh8sWVCNDzOdsxXEguJIc=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37", + "lastModified": 1761821581, + "narHash": "sha256-nLuc6jA7z+H/6bHPEBSOYPbz7RtvNCZiTKmYItJuBmM=", + "owner": "quickshell-mirror", + "repo": "quickshell", + "rev": "db1777c20b936a86528c1095cbcb1ebd92801402", "type": "github" }, "original": { - "owner": "cachix", - "repo": "git-hooks.nix", + "owner": "quickshell-mirror", + "repo": "quickshell", + "rev": "db1777c20b936a86528c1095cbcb1ebd92801402", "type": "github" } }, "root": { "inputs": { "home-manager": "home-manager", - "hyprland": "hyprland", "nixgl": "nixgl", - "nixpkgs": "nixpkgs_3" + "nixpkgs": "nixpkgs_2", + "quickshell": "quickshell" } }, "systems": { - "locked": { - "lastModified": 1689347949, - "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", - "owner": "nix-systems", - "repo": "default-linux", - "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default-linux", - "type": "github" - } - }, - "systems_2": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", @@ -494,47 +131,6 @@ "repo": "default", "type": "github" } - }, - "xdph": { - "inputs": { - "hyprland-protocols": [ - "hyprland", - "hyprland-protocols" - ], - "hyprlang": [ - "hyprland", - "hyprlang" - ], - "hyprutils": [ - "hyprland", - "hyprutils" - ], - "hyprwayland-scanner": [ - "hyprland", - "hyprwayland-scanner" - ], - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1760713634, - "narHash": "sha256-5HXelmz2x/uO26lvW7MudnadbAfoBnve4tRBiDVLtOM=", - "owner": "hyprwm", - "repo": "xdg-desktop-portal-hyprland", - "rev": "753bbbdf6a052994da94062e5b753288cef28dfb", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "xdg-desktop-portal-hyprland", - "type": "github" - } } }, "root": "root", diff --git a/sdata/dist-nix/home-manager/flake.nix b/sdata/dist-nix/home-manager/flake.nix index 3422f5c4b..d9b9a30b6 100644 --- a/sdata/dist-nix/home-manager/flake.nix +++ b/sdata/dist-nix/home-manager/flake.nix @@ -9,13 +9,17 @@ url = "github:nix-community/home-manager/release-25.05"; inputs.nixpkgs.follows = "nixpkgs"; }; - hyprland = { - url = "github:hyprwm/Hyprland"; - }; + #hyprland = { + # url = "github:hyprwm/Hyprland"; + #}; nixgl.url = "github:nix-community/nixGL"; + quickshell = { + url = "github:quickshell-mirror/quickshell/db1777c20b936a86528c1095cbcb1ebd92801402"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { nixpkgs, home-manager, nixgl, ... }: + outputs = { nixpkgs, home-manager, nixgl, quickshell, ... }: let home_attrs = rec { username = import ./username.nix; @@ -31,7 +35,7 @@ homeConfigurations = { illogical_impulse = home-manager.lib.homeManagerConfiguration { inherit pkgs; - extraSpecialArgs = { inherit nixgl home_attrs; }; + extraSpecialArgs = { inherit home_attrs nixgl quickshell; }; modules = [ ./home.nix ]; diff --git a/sdata/dist-nix/home-manager/home.nix b/sdata/dist-nix/home-manager/home.nix index 39d84584e..63a895af0 100644 --- a/sdata/dist-nix/home-manager/home.nix +++ b/sdata/dist-nix/home-manager/home.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, nixgl, home_attrs, ... }: +{ config, lib, pkgs, nixgl, quickshell, home_attrs, ... }: { programs.home-manager.enable = true; nixGL.packages = nixgl.packages; @@ -10,6 +10,7 @@ xdg-desktop-portal-gnome xdg-desktop-portal-gtk xdg-desktop-portal-wlr + kdePackages.xdg-desktop-portal-kde ]; config.hyprland = { default = [ "hyprland" "gtk" ]; @@ -21,7 +22,6 @@ ## Allow fontconfig to discover fonts in home.packages fonts.fontconfig.enable = true; - # home.sessionVariables.NIXOS_OZONE_WL = "1"; wayland.windowManager.hyprland = { ## Make sure home-manager not generate ~/.config/hypr/hyprland.conf systemd.enable = false; plugins = []; settings = {}; extraConfig = ""; @@ -30,44 +30,182 @@ package = config.lib.nixGL.wrap pkgs.hyprland; }; + + home = { packages = with pkgs; [ ##### Sure ##### ## Basic cli tool ## inetutils: provides hostname, ifconfig, ping, etc. ## libnotify: provides notify-send - jq rsync inetutils libnotify - ## Media related - brightnessctl pavucontrol - ## Clipboard/Emoji - wl-clipboard cliphist - ## Terminal and shell - foot cowsay lolcat + inetutils libnotify - ##### Fonts/Icons/Cursors/Decoration ##### - fontconfig + ##### Other MISC ##### + dbus xorg.xlsclients # some basic things + foot # Used in Quickshell and Hyprland config; its config is also included + kdePackages.kconfig # provide kwriteconfig6, used in install script - ##### Other basic things ##### - dbus xorg.xlsclients networkmanager ##### Not work, to be solved ##### - # swaylock pamtester + # hyprlock pamtester - # TODO: migrate all packages from dist-arch. Note that for each package, must know why it's needed and how it's used specifically, cuz things may be need tweak to properly use the package installed by Nix, especially those have hardcoded path /usr/* . + # NOTE: below are migrated from dist-arch. For each package, must know why it's needed and how it's used specifically, cuz things may be need tweak to properly use the package installed by Nix, for example those have hardcoded path /usr/* . ### illogical-impulse-audio - libcava #cava - lxqt.pavucontrol-qt #pavucontrol-qt + libcava #cava (Used in Quickshell config) + lxqt.pavucontrol-qt #pavucontrol-qt (Used in Hyprland and Quickshell config) wireplumber #wireplumber (not explicitly used) - pipewire #pipewire-pulse - libdbusmenu-gtk3 #libdbusmenu-gtk3 (not explicitly used) - playerctl #playerctl + pipewire #pipewire-pulse (not explicitly used) + libdbusmenu-gtk3 #libdbusmenu-gtk3 (not explicitly used) + playerctl #playerctl (Used in Hyprland and Quickshell config) + ### illogical-impulse-backlight - # TODO: geoclue is used in https://github.com/end-4/dots-hyprland/blob/0551c010b586dbf5578c32de2735698cca0801a7/dots/.config/hypr/hyprland/scripts/start_geoclue_agent.sh with hardcoded absolute path to search the agent. Below will not work without futher tweaks in that start_geoclue_agent.sh - geoclue2 # geoclue - brightnessctl # brightnessctl - ddcutil # ddcutil + (geoclue2.override { withDemoAgent = true; }) #geoclue (which demo agent used in Quickshell config) + brightnessctl #brightnessctl (Used in Hyprland and Quickshell config) + ddcutil #ddcutil (Used in Quickshell config) + + + ### illogical-impulse-basic + bc #bc (Used in quickshell/ii/scripts/colors/switchwall.sh for example) + uutils-coreutils-noprefix #coreutils (Too many executables involved, not sure where been used) + cliphist #cliphist (Used in Hyprland and Quickshell config) + cmake #cmake (Used in building quickshell and MicroTeX) + curlFull #curl (Used in Quickshell config) + wget #wget (Used in Quickshell config) + ripgrep #ripgrep (Not sure where been used) + jq #jq (Widely used) + xdg-user-dirs #xdg-user-dirs (Used in Hyprland and Quickshell config) + rsync #rsync (Used in install script) + yq-go #go-yq (Used in install script) + + + ### illogical-impulse-bibata-modern-classic-bin + bibata-cursors #https://github.com/ful1e5/Bibata_Cursor (Used in Hyprland config, not necessary) + + + ### illogical-impulse-fonts-themes + adw-gtk3 #adw-gtk-theme-git (https://github.com/lassekongo83/adw-gtk3) (Used in Quickshell config) + kdePackages.breeze kdePackages.breeze-icons #breeze (Used in kdeglobals config) + #breeze-plus (https://github.com/mjkim0727/breeze-plus) (TODO: Not available as nixpkg) (Used in kde-material-you-colors config) + darkly darkly-qt5 #darkly-bin (darkly is supposed to be set as the theme for Qt apps, just have not figured out how to properly set it yet.) + eza #eza (Used in Fish config: `alias ls 'eza --icons'`) + #fish (Install via system PM instead) + fontconfig #fontconfig (Basic thing) + kitty #kitty (Used in fuzzel, Hyprland, kdeglobals and Quickshell config; kitty config is also included as dots) + matugen #matugen-bin (Used in Quickshell) + #otf-space-grotesk (https://events.ccc.de/congress/2024/infos/styleguide.html) (TODO: Not available as Nixpkg) (Used in Quickshell and matugen config) + starship #starship (Used in Fish config) + #ttf-gabarito-git (Font name: Gabarito) (Used in fuzzel and Quickshell config) (TODO: Not available as Nixpkg) + nerd-fonts.jetbrains-mono #ttf-jetbrains-mono-nerd (Font name: JetBrains Mono NF, JetBrainsMono Nerd Font) (Used in foot, kdeglobals, kitty, qt5ct, qt6ct and Quickshell config) + material-symbols #ttf-material-symbols-variable-git (Font name: Material Symbols Rounded, Material Symbols Outlined) (Used in Hyprland, matugen, Quickshell and wlogout config) + #ttf-readex-pro (Font name: Readex Pro) (Used in Quickshell config) (TODO: seems not available as nixpkg) + roboto-flex #ttf-roboto-flex (Font name: Roboto Flex) (Used in Hyprland, matugen and Quickshell config) + rubik #ttf-rubik-vf (Font name: Rubik, Rubik Light) (Used in Hyprland, kdeglobals, matugen, qt5ct, qt6ct and Quickshell config) + twemoji-color-font #ttf-twemoji (Not explicitly used, but it may help as fallback for displaying emoji charaters) + + + ### illogical-impulse-hyprland + hypridle #hypridle (Used for loginctl to lock session) + #hyprland (Need NixGL, included elsewhere) + #hyprlock (Should not be installed via Nix) + hyprpicker #hyprpicker (Used in Hyprland and Quickshell config) + hyprsunset #hyprsunset (Used in Quickshell config) + #xdg-desktop-portal-hyprland (DUPLICATE) + wl-clipboard #wl-clipboard (Surely needed) + + + ### illogical-impulse-kde + kdePackages.bluedevil #bluedevil (Seems not being used anywhere, maybe a part of KDE settings panel) + #gnome-keyring #gnome-keyring (TODO: Install via system PM instead) (Provide executable gnome-keyring-daemon, used in Hyprland and Quickshell config) + networkmanager #networkmanager + kdePackages.plasma-nm #plasma-nm (Seems not being used anywhere, maybe a part of KDE settings panel) + #polkit-kde-agent (TODO: Install via system PM instead) + kdePackages.dolphin #dolphin (Used in Hyprland and Quickshell config) + kdePackages.systemsettings #systemsettings (Used in Hyprland keybinds.conf) + + + ### illogical-impulse-microtex-git + # This package will be installed as /opt/MicroTeX + #MicroTeX#https://github.com/NanoMichael/MicroTeX + # TODO: Not available as nixpkg + + + ### illogical-impulse-oneui4-icons-git + #OneUI4-Icons#https://github.com/end-4/OneUI4-Icons + # TODO: Custom repo, need to make a package + + + ### illogical-impulse-portal + #xdg-desktop-portal (Included elsewhere) + #xdg-desktop-portal-kde (Included elsewhere) + #xdg-desktop-portal-gtk (Included elsewhere) + #xdg-desktop-portal-hyprland (Included elsewhere) + + + ### illogical-impulse-python + #clang (Some python package may need this to be built, e.g. #1235; However when cmake is installed by Nix, then pkg-config, cairo etc will be used but they can only be accessible in Nix development environment for example nix-shell, nix develop, etc. See `sdata/uv/shell.nix`. ) + uv #uv (Used for python venv) + gtk4 #gtk4 (Not explicitly used) + libadwaita #libadwaita (Not explicitly used) + libsoup_3 #libsoup3 (Not explicitly used) + libportal-gtk4 #libportal-gtk4 (Not explicitly used) + gobject-introspection #gobject-introspection (Not explicitly used) + + + ### illogical-impulse-quickshell-git + #quickshell.packages.x86_64-linux.default (NixGL applicable, included elsewhere) + + + ### illogical-impulse-screencapture + hyprshot #hyprshot (Used in Hyprland keybinds.conf as fallback) + slurp #slurp (Used in Hyprland and Quickshell config) + swappy #swappy (Used in Quickshell config) + tesseract #tesseract (Used in Quickshell and Hyprland config) + #tesseract-data-eng (Used as data for tesseract) (TODO: Seems not available as nixpkg) + wf-recorder #wf-recorder (Used in Quickshell config) + + + ### illogical-impulse-toolkit + kdePackages.kdialog #kdialog (Used in Quickshell config) + # https://nixos.wiki/wiki/Qt + # https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/libraries/qt-6/srcs.nix + qt6.qt5compat #qt6-5compat + #qt6.qtimageformats (TODO: really?) #qt6-avif-image-plugin + qt6.qtbase #qt6-base + qt6.qtdeclarative #qt6-declarative + qt6.qtimageformats #qt6-imageformats + qt6.qtmultimedia #qt6-multimedia + qt6.qtpositioning #qt6-positioning + qt6.qtquicktimeline #qt6-quicktimeline + qt6.qtsensors #qt6-sensors + qt6.qtsvg #qt6-svg + qt6.qttools #qt6-tools + qt6.qttranslations #qt6-translations + qt6.qtvirtualkeyboard #qt6-virtualkeyboard + qt6.qtwayland #qt6-wayland + kdePackages.syntax-highlighting #syntax-highlighting (Used in Quickshell config) + upower #upower (Used in Quickshell config) + wtype #wtype (Used in Hyprland scripts/fuzzel-emoji.sh) + ydotool #ydotool (Used in Quickshell config) + + + ### illogical-impulse-widgets + fuzzel #fuzzel (Used in Hyprland and Quickshell config; its config is also included) + glib #glib2 (Provide executable gsettings) (Used in install script, also in matugen and quickshell config) + imagemagick #imagemagick (Provide executable: magick) (Used in Quickshell config) + #hypridle (DUPLICATE) + #hyprutils (DUPLICATE) + #hyprlock (DUPLICATE) + #hyprpicker (DUPLICATE) + songrec #songrec (Used in Quickshell config) + translate-shell #translate-shell (Used in Quickshell config) + wlogout #wlogout (Used in Hyprland config) + + ] + ++ [ + #(config.lib.nixGL.wrap pkgs.hyprland) + (config.lib.nixGL.wrap quickshell.packages.x86_64-linux.default) ]; }//home_attrs; } diff --git a/sdata/dist-nix/install-deps.sh b/sdata/dist-nix/install-deps.sh index 726a51259..731258e87 100644 --- a/sdata/dist-nix/install-deps.sh +++ b/sdata/dist-nix/install-deps.sh @@ -1,6 +1,14 @@ # This script is meant to be sourced. # It's not for directly running. +function vianix-warning(){ + printf "${STY_YELLOW}Currently \"--via-nix\" will run:\n" + printf " home-manager switch --flake .#illogical_impulse\n" + printf "If you are already using home-manager, it may override your current config,\n" + printf "despite that this should be reversible.\n" + pause +} + function install_home-manager(){ # https://nix-community.github.io/home-manager/index.xhtml#sec-install-standalone local cmd=home-manager @@ -16,6 +24,11 @@ function install_home-manager(){ command -v $cmd && return echo "Failed in installing $cmd." echo "Please install it by yourself and then retry." + echo "" + echo "Hint: It's also possible that the installation is actually successful," + echo "but your \"\$PATH\" is not properly set." + echo "This can happen when you have used \"su user\" to switch user." + echo "If this is the problem, use \"su - user\" instead." return 1 } function install_nix(){ @@ -48,8 +61,8 @@ function install_curl(){ echo "Please install it by yourself and then retry." return 1 } -function install_zsh(){ - local cmd=zsh +function install_fish(){ + local cmd=fish if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then x sudo pacman -Syu @@ -99,15 +112,18 @@ function hm_deps(){ ################################################## ################################################## + +vianix-warning + if ! command -v curl >/dev/null 2>&1;then echo -e "${STY_YELLOW}[$0]: \"curl\" not found.${STY_RST}" showfun install_curl v install_curl fi -if ! command -v zsh >/dev/null 2>&1;then - echo -e "${STY_YELLOW}[$0]: \"zsh\" not found.${STY_RST}" - showfun install_zsh - v install_zsh +if ! command -v fish >/dev/null 2>&1;then + echo -e "${STY_YELLOW}[$0]: \"fish\" not found.${STY_RST}" + showfun install_fish + v install_fish fi if ! command -v swaylock >/dev/null 2>&1;then echo -e "${STY_YELLOW}[$0]: \"swaylock\" not found.${STY_RST}" diff --git a/sdata/lib/package-installers.sh b/sdata/lib/package-installers.sh index 54da9833d..5f71cd7c1 100644 --- a/sdata/lib/package-installers.sh +++ b/sdata/lib/package-installers.sh @@ -5,21 +5,6 @@ # This file is provided for any distros, mainly non-Arch(based) distros. -install-agsv1(){ - x mkdir -p $REPO_ROOT/cache/agsv1 - x cd $REPO_ROOT/cache/agsv1 - try git init -b main - try git remote add origin https://github.com/Aylur/ags.git - x git pull origin main && git submodule update --init --recursive - x git fetch --tags - x git checkout v1.9.0 - x npm install - x meson setup build # --reconfigure - x meson install -C build - x sudo mv /usr/local/bin/ags{,v1} - x cd $REPO_ROOT -} - install-Rubik(){ x mkdir -p $REPO_ROOT/cache/Rubik x cd $REPO_ROOT/cache/Rubik @@ -68,8 +53,8 @@ install-bibata(){ x cd $REPO_ROOT/cache/bibata-cursor name="Bibata-Modern-Classic" file="$name.tar.xz" - # Use axel because `curl -O` always downloads a file with 0 byte size, idk why - x axel https://github.com/ful1e5/Bibata_Cursor/releases/latest/download/$file + try rm $file + x curl -JLO https://github.com/ful1e5/Bibata_Cursor/releases/latest/download/$file tar -xf $file x sudo mkdir -p /usr/local/share/icons x sudo cp -r $name /usr/local/share/icons @@ -103,6 +88,10 @@ install-python-packages(){ # we need python 3.12 https://github.com/python-pillow/Pillow/issues/8089 x uv venv --prompt .venv $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV) -p 3.12 x source $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate - x uv pip install -r sdata/uv/requirements.txt + if [[ "$INSTALL_VIA_NIX" = true ]]; then + x nix-shell ${REPO_ROOT}/sdata/uv/shell.nix --run "uv pip install -r ${REPO_ROOT}/sdata/uv/requirements.txt" + else + x uv pip install -r ${REPO_ROOT}/sdata/uv/requirements.txt + fi x deactivate } diff --git a/sdata/subcmd-install/1.deps-selector.sh b/sdata/subcmd-install/1.deps-selector.sh index 27661925e..dec23c463 100644 --- a/sdata/subcmd-install/1.deps-selector.sh +++ b/sdata/subcmd-install/1.deps-selector.sh @@ -65,9 +65,15 @@ esac # Helpful link(s): # http://stackoverflow.com/questions/29581754 # https://github.com/which-distro/os-release -export OS_RELEASE_FILE=${OS_RELEASE_FILE:-/etc/os-release} -test -f ${OS_RELEASE_FILE} || \ - ( echo "${OS_RELEASE_FILE} does not exist. Aborting..." ; exit 1 ; ) +OS_RELEASE_FILE_CUSTOM="${REPO_ROOT}/os-release" +if test -f "${OS_RELEASE_FILE_CUSTOM}"; then + printf "${STY_YELLOW}Warning: using custom os-release file \"${OS_RELEASE_FILE_CUSTOM}\".${STY_RST}\n" + OS_RELEASE_FILE="${OS_RELEASE_FILE_CUSTOM}" +elif test -f /etc/os-release; then + OS_RELEASE_FILE=/etc/os-release +else + printf "${STY_RED}/etc/os-release does not exist, aborting...${STY_RST}\n" ; exit 1 +fi export OS_DISTRO_ID=$(awk -F'=' '/^ID=/ { gsub("\"","",$2); print tolower($2) }' ${OS_RELEASE_FILE} 2> /dev/null) export OS_DISTRO_ID_LIKE=$(awk -F'=' '/^ID_LIKE=/ { gsub("\"","",$2); print tolower($2) }' ${OS_RELEASE_FILE} 2> /dev/null) diff --git a/sdata/subcmd-install/3.files-exp.sh b/sdata/subcmd-install/3.files-exp.sh index 8f0bcd8f9..0ff62c0e3 100644 --- a/sdata/subcmd-install/3.files-exp.sh +++ b/sdata/subcmd-install/3.files-exp.sh @@ -9,6 +9,7 @@ # TODO: add --exp-files-regen Force copy the default config to ${EXP_FILE_PATH} (auto do this when not existed) # TODO: Implement versioning, i.e. when user-defined yaml config file has version number mismatch with the default one, produce error. If only minor version number is not the same, the error can be ommitted via --exp-file-no-strict . # TODO: add --exp-files-no-strict Ignore error when minor version number is not the same +# TODO: When --via-nix is specified, use dots-extra/vianix/hypridle.conf instead # # Stage 2 todos: # TODO: Implement bool key symlink (both read-write and read-only), when the value of `symlink` is true, then instead using `rsync` or `cp`, use `ln`. diff --git a/sdata/subcmd-install/3.files-legacy.sh b/sdata/subcmd-install/3.files-legacy.sh index 608b4ac6d..6f67f0a16 100644 --- a/sdata/subcmd-install/3.files-legacy.sh +++ b/sdata/subcmd-install/3.files-legacy.sh @@ -79,13 +79,18 @@ case $SKIP_HYPRLAND in v cp dots/.config/hypr/hyprland.conf $t fi t="$XDG_CONFIG_HOME/hypr/hypridle.conf" + if [[ "$INSTALL_VIA_NIX" = true ]]; then + s=dots-extra/vianix/hypridle.conf + else + s=dots/.config/hypr/hypridle.conf + fi if [ -f $t ];then echo -e "${STY_BLUE}[$0]: \"$t\" already exists.${STY_RST}" - v cp -f dots/.config/hypr/hypridle.conf $t.new + v cp -f $s $t.new existed_hypridle_conf=y else echo -e "${STY_YELLOW}[$0]: \"$t\" does not exist yet.${STY_RST}" - v cp dots/.config/hypr/hypridle.conf $t + v cp $s $t existed_hypridle_conf=n fi t="$XDG_CONFIG_HOME/hypr/hyprlock.conf" diff --git a/sdata/uv/shell.nix b/sdata/uv/shell.nix new file mode 100644 index 000000000..46f9f8f65 --- /dev/null +++ b/sdata/uv/shell.nix @@ -0,0 +1,12 @@ +{ pkgs ? import {} }: +pkgs.mkShell { + buildInputs = with pkgs; [ + pkg-config + meson + ninja + cairo + dbus + dbus-glib + glib + ]; +}