Merge branch 'end-4:main' into keybinds-settings

This commit is contained in:
Madjid Taha
2025-11-04 17:21:27 +01:00
committed by GitHub
89 changed files with 1061 additions and 1212 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ Widget system: Quickshell | Support: Yes
| AI, settings app | Some widgets | | AI, settings app | Some widgets |
|:---|:---------------| |:---|:---------------|
| <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/7b98a354-4489-4a46-aa6a-d08616e77399" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/6eba0d57-2606-4cea-8993-e6f169e82e70" /> | | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/ea0154a1-e984-4bb6-a424-23247cefe3c6" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/6eba0d57-2606-4cea-8993-e6f169e82e70" /> |
| Window management | Weeb power | | Window management | Weeb power |
| <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/e77a7c96-1905-4126-a2a0-434f818825a2" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/c8544e99-8881-477f-b83a-d6e35c0184a1" /> | | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/e77a7c96-1905-4126-a2a0-434f818825a2" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/c8544e99-8881-477f-b83a-d6e35c0184a1" /> |
+1
View File
@@ -5,3 +5,4 @@ __pycache__/
*.py[cod] *.py[cod]
dots/.config/quickshell/ii/.qmlls.ini dots/.config/quickshell/ii/.qmlls.ini
.update-lock .update-lock
/os-release
+4
View File
@@ -58,6 +58,9 @@ ii_check_venv() {
ii_check_quickshell_version() { ii_check_quickshell_version() {
pacman -Q | grep -E 'quickshell|qt6-base' pacman -Q | grep -E 'quickshell|qt6-base'
} }
ii_check_PKGBUILD_version() {
pacman -Q | grep '^illogical-impulse-'
}
e "Checking git repo info" e "Checking git repo info"
x git remote get-url origin x git remote get-url origin
@@ -88,6 +91,7 @@ x ls -l ~/.local/state/quickshell/.venv
e "Checking versions" e "Checking versions"
x Hyprland --version x Hyprland --version
x ii_check_quickshell_version x ii_check_quickshell_version
x ii_check_PKGBUILD_version
e "Finished. Output saved as \"$output_file\"." 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 if ! command -v curl 2>&1 >>/dev/null ;then echo "\"curl\" not found, pastebin upload unavailable.";exit;fi
+1
View File
@@ -0,0 +1 @@
This folder contains tweakd configs when --via-nix is specified.
+26
View File
@@ -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
}
+1 -1
View File
@@ -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=, 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=, 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] bindle=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%- # [hidden]
bindl = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SINK@ toggle # [hidden] bindl = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SINK@ toggle # [hidden]
+1
View File
@@ -144,6 +144,7 @@ layerrule = animation slide bottom, quickshell:osk
layerrule = noanim, quickshell:polkit layerrule = noanim, quickshell:polkit
layerrule = xray 0, quickshell:popup # No weird color for bar tooltips (this in theory should suffice) 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: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:regionSelector
layerrule = noanim, quickshell:screenshot layerrule = noanim, quickshell:screenshot
layerrule = blur, quickshell:session layerrule = blur, quickshell:session
@@ -7,13 +7,16 @@ if pgrep -f 'geoclue-2.0/demos/agent' > /dev/null; then
fi fi
# List of known possible GeoClue agent paths # List of known possible GeoClue agent paths
AGENT_PATHS=" AGENT_PATHS=(
/usr/libexec/geoclue-2.0/demos/agent /usr/libexec/geoclue-2.0/demos/agent
/usr/lib/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 # Find the first valid agent path
for path in $AGENT_PATHS; do for path in "${AGENT_PATHS[@]}"; do
if [ -x "$path" ]; then if [ -x "$path" ]; then
echo "Starting GeoClue agent from: $path" echo "Starting GeoClue agent from: $path"
"$path" & # starts in the background "$path" & # starts in the background
@@ -334,7 +334,6 @@ Variants {
font { font {
pixelSize: Appearance.font.pixelSize.normal pixelSize: Appearance.font.pixelSize.normal
weight: 350 weight: 350
italic: true
} }
color: bgRoot.colText color: bgRoot.colText
style: Text.Raised style: Text.Raised
@@ -1,6 +1,7 @@
import qs import qs
import qs.services import qs.services
import qs.modules.common import qs.modules.common
import qs.modules.common.models
import qs.modules.common.widgets import qs.modules.common.widgets
import qs.modules.common.functions import qs.modules.common.functions
import QtQuick import QtQuick
@@ -163,12 +164,12 @@ Item {
horizontalCenter: vertical ? parent.horizontalCenter : undefined horizontalCenter: vertical ? parent.horizontalCenter : undefined
} }
// idx1 is the "leading" indicator position, idx2 is the "following" one AnimatedTabIndexPair {
// The former animates faster than the latter, see the NumberAnimations below id: idxPair
property real idx1: workspaceIndexInGroup index: root.workspaceIndexInGroup
property real idx2: workspaceIndexInGroup }
property real indicatorPosition: Math.min(idx1, idx2) * workspaceButtonWidth + root.activeWorkspaceMargin property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * workspaceButtonWidth + root.activeWorkspaceMargin
property real indicatorLength: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2 property real indicatorLength: Math.abs(idxPair.idx1 - idxPair.idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2
property real indicatorThickness: workspaceButtonWidth - root.activeWorkspaceMargin * 2 property real indicatorThickness: workspaceButtonWidth - root.activeWorkspaceMargin * 2
x: root.vertical ? null : indicatorPosition x: root.vertical ? null : indicatorPosition
@@ -176,18 +177,6 @@ Item {
y: root.vertical ? indicatorPosition : null y: root.vertical ? indicatorPosition : null
implicitHeight: root.vertical ? indicatorLength : indicatorThickness 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 // Workspaces - numbers
@@ -31,7 +31,6 @@ Scope { // Scope
sourceComponent: PanelWindow { // Window sourceComponent: PanelWindow { // Window
id: cheatsheetRoot id: cheatsheetRoot
visible: cheatsheetLoader.active visible: cheatsheetLoader.active
property int selectedTab: 0
anchors { anchors {
top: true top: true
@@ -76,7 +75,7 @@ Scope { // Scope
border.width: 1 border.width: 1
border.color: Appearance.colors.colLayer0Border border.color: Appearance.colors.colLayer0Border
radius: Appearance.rounding.windowRounding radius: Appearance.rounding.windowRounding
property real padding: 30 property real padding: 20
implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2 implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2
implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2 implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2
@@ -86,16 +85,16 @@ Scope { // Scope
} }
if (event.modifiers === Qt.ControlModifier) { if (event.modifiers === Qt.ControlModifier) {
if (event.key === Qt.Key_PageDown) { if (event.key === Qt.Key_PageDown) {
cheatsheetRoot.selectedTab = Math.min(cheatsheetRoot.selectedTab + 1, root.tabButtonList.length - 1); tabBar.incrementCurrentIndex();
event.accepted = true; event.accepted = true;
} else if (event.key === Qt.Key_PageUp) { } else if (event.key === Qt.Key_PageUp) {
cheatsheetRoot.selectedTab = Math.max(cheatsheetRoot.selectedTab - 1, 0); tabBar.decrementCurrentIndex();
event.accepted = true; event.accepted = true;
} else if (event.key === Qt.Key_Tab) { } 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; event.accepted = true;
} else if (event.key === Qt.Key_Backtab) { } 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; event.accepted = true;
} }
} }
@@ -129,23 +128,15 @@ Scope { // Scope
ColumnLayout { // Real content ColumnLayout { // Real content
id: cheatsheetColumnLayout id: cheatsheetColumnLayout
anchors.centerIn: parent anchors.centerIn: parent
spacing: 20 spacing: 10
StyledText { Toolbar {
id: cheatsheetTitle
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
font { enableShadow: false
family: Appearance.font.family.title ToolbarTabBar {
pixelSize: Appearance.font.pixelSize.title id: tabBar
variableAxes: Appearance.font.variableAxes.title tabButtonList: root.tabButtonList
} currentIndex: swipeView.currentIndex
text: Translation.tr("Cheat sheet")
}
PrimaryTabBar { // Tab strip
id: tabBar
tabButtonList: root.tabButtonList
Synchronizer on currentIndex {
property alias source: cheatsheetRoot.selectedTab
} }
} }
@@ -154,26 +145,11 @@ Scope { // Scope
Layout.topMargin: 5 Layout.topMargin: 5
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
currentIndex: tabBar.currentIndex
spacing: 10 spacing: 10
Behavior on implicitWidth { implicitWidth: Math.max.apply(null, contentChildren.map(child => child.implicitWidth || 0))
id: contentWidthBehavior implicitHeight: Math.max.apply(null, contentChildren.map(child => child.implicitHeight || 0))
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;
}
clip: true clip: true
layer.enabled: true layer.enabled: true
@@ -11,6 +11,7 @@ Item {
Column { Column {
id: mainLayout id: mainLayout
anchors.centerIn: parent
spacing: root.spacing spacing: root.spacing
Repeater { // Main table rows Repeater { // Main table rows
@@ -7,8 +7,8 @@ RippleButton {
id: root id: root
required property var element required property var element
opacity: element.type != "empty" ? 1 : 0 opacity: element.type != "empty" ? 1 : 0
implicitHeight: 60 implicitHeight: 70
implicitWidth: 60 implicitWidth: 70
colBackground: Appearance.colors.colLayer2 colBackground: Appearance.colors.colLayer2
buttonRadius: Appearance.rounding.small buttonRadius: Appearance.rounding.small
@@ -373,6 +373,7 @@ Singleton {
property real scale: 0.18 // Relative to screen size property real scale: 0.18 // Relative to screen size
property real rows: 2 property real rows: 2
property real columns: 5 property real columns: 5
property bool centerIcons: true
} }
property JsonObject regionSelector: JsonObject { property JsonObject regionSelector: JsonObject {
@@ -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
}
}
}
@@ -13,7 +13,7 @@ ToolbarButton {
contentItem: Row { contentItem: Row {
anchors.centerIn: parent anchors.centerIn: parent
spacing: 6 spacing: 4
MaterialSymbol { MaterialSymbol {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@@ -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
}
}
@@ -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)
}
}
}
}
}
@@ -10,6 +10,7 @@ import qs.modules.common.widgets
Item { Item {
id: root id: root
property bool enableShadow: true
property real padding: 8 property real padding: 8
property alias colBackground: background.color property alias colBackground: background.color
property alias spacing: toolbarLayout.spacing property alias spacing: toolbarLayout.spacing
@@ -18,15 +19,20 @@ Item {
implicitHeight: background.implicitHeight implicitHeight: background.implicitHeight
property alias radius: background.radius property alias radius: background.radius
StyledRectangularShadow { Loader {
target: background active: root.enableShadow
anchors.fill: background
sourceComponent: StyledRectangularShadow {
target: background
anchors.fill: undefined
}
} }
Rectangle { Rectangle {
id: background id: background
anchors.fill: parent anchors.fill: parent
color: Appearance.m3colors.m3surfaceContainer color: Appearance.m3colors.m3surfaceContainer
implicitHeight: Math.max(toolbarLayout.implicitHeight + root.padding * 2, 56) implicitHeight: 56
implicitWidth: toolbarLayout.implicitWidth + root.padding * 2 implicitWidth: toolbarLayout.implicitWidth + root.padding * 2
radius: height / 2 radius: height / 2
@@ -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
}
}
}
}
@@ -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
}
}
}
@@ -13,10 +13,16 @@ import Quickshell.Hyprland
Scope { Scope {
id: root id: root
Process {
id: unlockKeyringProc
onExited: (exitCode, exitStatus) => {
KeyringStorage.fetchKeyringData();
}
}
function unlockKeyring() { function unlockKeyring() {
Quickshell.execDetached({ unlockKeyringProc.exec({
environment: ({ environment: ({
UNLOCK_PASSWORD: root.currentText "UNLOCK_PASSWORD": lockContext.currentText
}), }),
command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")] command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")]
}) })
@@ -24,7 +30,7 @@ Scope {
property var windowData: [] property var windowData: []
function saveWindowPositionAndTile() { 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 = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id))
root.windowData.forEach(w => { root.windowData.forEach(w => {
Hyprland.dispatch(`pseudo address:${w.address}`) 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(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`)
Hyprland.dispatch(`pseudo 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. // 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 { Connections {
target: Config target: Config
function onReadyChanged() { function onReadyChanged() {
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) { root.initIfReady();
Hyprland.dispatch("global quickshell:lock")
}
} }
} }
Connections { Connections {
target: Persistent target: Persistent
function onReadyChanged() { function onReadyChanged() {
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) { root.initIfReady();
Hyprland.dispatch("global quickshell:lock")
}
} }
} }
} }
@@ -38,6 +38,7 @@ Scope {
root.resetTargetAction(); root.resetTargetAction();
root.clearText(); root.clearText();
root.unlockInProgress = false; root.unlockInProgress = false;
stopFingerPam();
} }
Timer { Timer {
@@ -69,7 +70,9 @@ Scope {
} }
function stopFingerPam() { function stopFingerPam() {
fingerPam.abort(); if (fingerPam.running) {
fingerPam.abort();
}
} }
Process { Process {
@@ -119,6 +119,7 @@ MouseArea {
ToolbarTextField { ToolbarTextField {
id: passwordBox id: passwordBox
Layout.rightMargin: -Layout.leftMargin
placeholderText: GlobalStates.screenUnlockFailed ? Translation.tr("Incorrect password") : Translation.tr("Enter password") placeholderText: GlobalStates.screenUnlockFailed ? Translation.tr("Incorrect password") : Translation.tr("Enter password")
// Style // Style
@@ -156,11 +157,11 @@ MouseArea {
// Shake when wrong password // Shake when wrong password
SequentialAnimation { SequentialAnimation {
id: wrongPasswordShakeAnim id: wrongPasswordShakeAnim
NumberAnimation { target: passwordBox; property: "x"; to: -30; duration: 50 } NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: -30; duration: 50 }
NumberAnimation { target: passwordBox; property: "x"; to: 30; duration: 50 } NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: 30; duration: 50 }
NumberAnimation { target: passwordBox; property: "x"; to: -15; duration: 40 } NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: -15; duration: 40 }
NumberAnimation { target: passwordBox; property: "x"; to: 15; duration: 40 } NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: 15; duration: 40 }
NumberAnimation { target: passwordBox; property: "x"; to: 0; duration: 30 } NumberAnimation { target: passwordBox; property: "Layout.leftMargin"; to: 0; duration: 30 }
} }
Connections { Connections {
target: GlobalStates target: GlobalStates
@@ -101,7 +101,7 @@ Item { // Player instance
id: background id: background
anchors.fill: parent anchors.fill: parent
anchors.margins: Appearance.sizes.elevationMargin anchors.margins: Appearance.sizes.elevationMargin
color: blendedColors.colLayer0 color: ColorUtils.applyAlpha(blendedColors.colLayer0, 1)
radius: root.radius radius: root.radius
layer.enabled: true layer.enabled: true
@@ -43,10 +43,12 @@ Item { // Window
property bool hovered: false property bool hovered: false
property bool pressed: false property bool pressed: false
property var iconToWindowRatio: 0.35 property bool centerIcons: Config.options.overview.centerIcons
property var xwaylandIndicatorToIconRatio: 0.35 property real iconGapRatio: 0.06
property var iconToWindowRatioCompact: 0.6 property real iconToWindowRatio: centerIcons ? 0.35 : 0.15
property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing") 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 compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth
property bool indicateXWayland: windowData?.xwayland ?? false property bool indicateXWayland: windowData?.xwayland ?? false
@@ -109,14 +111,20 @@ Item { // Window
Image { Image {
id: windowIcon 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: { property var iconSize: {
// console.log("-=-=-", root.toplevel.title, "-=-=-") // console.log("-=-=-", root.toplevel.title, "-=-=-")
// console.log("Target window size:", targetWindowWidth, targetWindowHeight) // console.log("Target window size:", targetWindowWidth, targetWindowHeight)
// console.log("Icon ratio:", root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) // console.log("Icon ratio:", root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
// console.log("Scale:", root.monitorData.scale) // console.log("Scale:", root.monitorData.scale)
// console.log("Final:", Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / 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 // mipmap: true
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
@@ -65,18 +65,16 @@ Toolbar {
} }
} }
IconAndTextToolbarButton { ToolbarTabBar {
iconText: "activity_zone" id: tabBar
text: Translation.tr("Rect") tabButtonList: [
toggled: root.selectionMode === RegionSelection.SelectionMode.RectCorners {"icon": "activity_zone", "name": Translation.tr("Rect")},
onClicked: root.selectionMode = RegionSelection.SelectionMode.RectCorners {"icon": "gesture", "name": Translation.tr("Circle")}
} ]
currentIndex: root.selectionMode === RegionSelection.SelectionMode.RectCorners ? 0 : 1
IconAndTextToolbarButton { onCurrentIndexChanged: {
iconText: "gesture" root.selectionMode = currentIndex === 0 ? RegionSelection.SelectionMode.RectCorners : RegionSelection.SelectionMode.Circle;
text: Translation.tr("Circle") }
toggled: root.selectionMode === RegionSelection.SelectionMode.Circle
onClicked: root.selectionMode = RegionSelection.SelectionMode.Circle
} }
} }
@@ -17,7 +17,7 @@ PanelWindow {
color: "transparent" color: "transparent"
WlrLayershell.namespace: "quickshell:regionSelector" WlrLayershell.namespace: "quickshell:regionSelector"
WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
exclusionMode: ExclusionMode.Ignore exclusionMode: ExclusionMode.Ignore
anchors { anchors {
left: true left: true
@@ -961,6 +961,14 @@ ContentPage {
Config.options.overview.enable = checked; 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 { ConfigSpinBox {
icon: "loupe" icon: "loupe"
text: Translation.tr("Scale (%)") text: Translation.tr("Scale (%)")
@@ -13,27 +13,28 @@ import Quickshell.Io
Item { Item {
id: root id: root
property real padding: 4
property var inputField: messageInputField property var inputField: messageInputField
property string commandPrefix: "/" property string commandPrefix: "/"
property var suggestionQuery: "" property var suggestionQuery: ""
property var suggestionList: [] property var suggestionList: []
onFocusChanged: (focus) => { onFocusChanged: focus => {
if (focus) { if (focus) {
root.inputField.forceActiveFocus() root.inputField.forceActiveFocus();
} }
} }
Keys.onPressed: (event) => { Keys.onPressed: event => {
messageInputField.forceActiveFocus() messageInputField.forceActiveFocus();
if (event.modifiers === Qt.NoModifier) { if (event.modifiers === Qt.NoModifier) {
if (event.key === Qt.Key_PageUp) { if (event.key === Qt.Key_PageUp) {
messageListView.contentY = Math.max(0, messageListView.contentY - messageListView.height / 2) messageListView.contentY = Math.max(0, messageListView.contentY - messageListView.height / 2);
event.accepted = true event.accepted = true;
} else if (event.key === Qt.Key_PageDown) { } else if (event.key === Qt.Key_PageDown) {
messageListView.contentY = Math.min(messageListView.contentHeight - messageListView.height / 2, messageListView.contentY + messageListView.height / 2) messageListView.contentY = Math.min(messageListView.contentHeight - messageListView.height / 2, messageListView.contentY + messageListView.height / 2);
event.accepted = true event.accepted = true;
} }
} }
if ((event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier) && event.key === Qt.Key_O) { if ((event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier) && event.key === Qt.Key_O) {
@@ -45,21 +46,21 @@ Item {
{ {
name: "attach", name: "attach",
description: Translation.tr("Attach a file. Only works with Gemini."), description: Translation.tr("Attach a file. Only works with Gemini."),
execute: (args) => { execute: args => {
Ai.attachFile(args.join(" ").trim()); Ai.attachFile(args.join(" ").trim());
} }
}, },
{ {
name: "model", name: "model",
description: Translation.tr("Choose model"), description: Translation.tr("Choose model"),
execute: (args) => { execute: args => {
Ai.setModel(args[0]); Ai.setModel(args[0]);
} }
}, },
{ {
name: "tool", name: "tool",
description: Translation.tr("Set the tool to use for the model."), description: Translation.tr("Set the tool to use for the model."),
execute: (args) => { execute: args => {
// console.log(args) // console.log(args)
if (args.length == 0 || args[0] == "get") { if (args.length == 0 || args[0] == "get") {
Ai.addMessage(Translation.tr("Usage: %1tool TOOL_NAME").arg(root.commandPrefix), Ai.interfaceRole); Ai.addMessage(Translation.tr("Usage: %1tool TOOL_NAME").arg(root.commandPrefix), Ai.interfaceRole);
@@ -75,7 +76,7 @@ Item {
{ {
name: "prompt", name: "prompt",
description: Translation.tr("Set the system prompt for the model."), description: Translation.tr("Set the system prompt for the model."),
execute: (args) => { execute: args => {
if (args.length === 0 || args[0] === "get") { if (args.length === 0 || args[0] === "get") {
Ai.printPrompt(); Ai.printPrompt();
return; return;
@@ -86,9 +87,9 @@ Item {
{ {
name: "key", name: "key",
description: Translation.tr("Set API key"), description: Translation.tr("Set API key"),
execute: (args) => { execute: args => {
if (args[0] == "get") { if (args[0] == "get") {
Ai.printApiKey() Ai.printApiKey();
} else { } else {
Ai.setApiKey(args[0]); Ai.setApiKey(args[0]);
} }
@@ -97,25 +98,25 @@ Item {
{ {
name: "save", name: "save",
description: Translation.tr("Save chat"), description: Translation.tr("Save chat"),
execute: (args) => { execute: args => {
const joinedArgs = args.join(" ") const joinedArgs = args.join(" ");
if (joinedArgs.trim().length == 0) { if (joinedArgs.trim().length == 0) {
Ai.addMessage(Translation.tr("Usage: %1save CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole); Ai.addMessage(Translation.tr("Usage: %1save CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole);
return; return;
} }
Ai.saveChat(joinedArgs) Ai.saveChat(joinedArgs);
} }
}, },
{ {
name: "load", name: "load",
description: Translation.tr("Load chat"), description: Translation.tr("Load chat"),
execute: (args) => { execute: args => {
const joinedArgs = args.join(" ") const joinedArgs = args.join(" ");
if (joinedArgs.trim().length == 0) { if (joinedArgs.trim().length == 0) {
Ai.addMessage(Translation.tr("Usage: %1load CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole); Ai.addMessage(Translation.tr("Usage: %1load CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole);
return; return;
} }
Ai.loadChat(joinedArgs) Ai.loadChat(joinedArgs);
} }
}, },
{ {
@@ -128,10 +129,10 @@ Item {
{ {
name: "temp", 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."), 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) // console.log(args)
if (args.length == 0 || args[0] == "get") { if (args.length == 0 || args[0] == "get") {
Ai.printTemperature() Ai.printTemperature();
} else { } else {
const temp = parseFloat(args[0]); const temp = parseFloat(args[0]);
Ai.setTemperature(temp); 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 square brackets \\[\\int_0^\\infty \\frac{1}{x^2} dx = \\infty\\]
Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) 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 { } else {
Ai.addMessage(Translation.tr("Unknown command: ") + command, Ai.interfaceRole); Ai.addMessage(Translation.tr("Unknown command: ") + command, Ai.interfaceRole);
} }
} } else {
else {
Ai.sendUserMessage(inputText); Ai.sendUserMessage(inputText);
} }
// Always scroll to bottom when user sends a message // Always scroll to bottom when user sends a message
messageListView.positionViewAtEnd() messageListView.positionViewAtEnd();
} }
Process { Process {
@@ -223,16 +222,14 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
property string imageDecodeFileName: "image" property string imageDecodeFileName: "image"
property string imageDecodeFilePath: `${imageDecodePath}/${imageDecodeFileName}` property string imageDecodeFilePath: `${imageDecodePath}/${imageDecodeFileName}`
function handleEntry(entry: string) { function handleEntry(entry: string) {
imageDecodeFileName = parseInt(entry.match(/^(\d+)\t/)[1]) imageDecodeFileName = parseInt(entry.match(/^(\d+)\t/)[1]);
decodeImageAndAttachProc.exec(["bash", "-c", decodeImageAndAttachProc.exec(["bash", "-c", `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'`]);
`[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'`
])
} }
onExited: (exitCode, exitStatus) => { onExited: (exitCode, exitStatus) => {
if (exitCode === 0) { if (exitCode === 0) {
Ai.attachFile(imageDecodeFilePath); Ai.attachFile(imageDecodeFilePath);
} else { } 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 { ColumnLayout {
id: columnLayout id: columnLayout
anchors.fill: parent anchors {
fill: parent
RowLayout { // Status margins: root.padding
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)
}
} }
spacing: root.padding
Item { // Messages Item {
// Messages
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
layer.enabled: 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 { ScrollEdgeFade {
z: 1 z: 1
target: messageListView target: messageListView
@@ -332,16 +355,20 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
anchors.fill: parent anchors.fill: parent
spacing: 10 spacing: 10
popin: false popin: false
topMargin: statusBg.implicitHeight + statusBg.anchors.topMargin * 2
touchpadScrollFactor: Config.options.interactions.scrolling.touchpadScrollFactor * 1.4 touchpadScrollFactor: Config.options.interactions.scrolling.touchpadScrollFactor * 1.4
mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4 mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4
property int lastResponseLength: 0 property int lastResponseLength: 0
onContentHeightChanged: { onContentHeightChanged: {
if (atYEnd) Qt.callLater(positionViewAtEnd); if (atYEnd)
Qt.callLater(positionViewAtEnd);
} }
onCountChanged: { // Auto-scroll when new messages are added onCountChanged: {
if (atYEnd) Qt.callLater(positionViewAtEnd); // Auto-scroll when new messages are added
if (atYEnd)
Qt.callLater(positionViewAtEnd);
} }
add: null // Prevent function calls from being janky 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 required property int index
messageIndex: index messageIndex: index
messageData: { messageData: {
Ai.messageByID[modelData] Ai.messageByID[modelData];
} }
messageInputField: root.inputField messageInputField: root.inputField
} }
@@ -393,8 +420,8 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
Repeater { Repeater {
id: suggestionRepeater id: suggestionRepeater
model: { model: {
suggestions.selectedIndex = 0 suggestions.selectedIndex = 0;
return root.suggestionList.slice(0, 10) return root.suggestionList.slice(0, 10);
} }
delegate: ApiCommandButton { delegate: ApiCommandButton {
id: commandButton id: commandButton
@@ -413,7 +440,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
} }
} }
onClicked: { 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 id: inputWrapper
property real spacing: 5 property real spacing: 5
Layout.fillWidth: true Layout.fillWidth: true
radius: Appearance.rounding.small radius: Appearance.rounding.normal - root.padding
color: Appearance.colors.colLayer1 color: Appearance.colors.colLayer2
implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin + commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + spacing, 45) + (attachedFileIndicator.implicitHeight + spacing + attachedFileIndicator.anchors.topMargin)
+ commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + spacing, 45)
+ (attachedFileIndicator.implicitHeight + spacing + attachedFileIndicator.anchors.topMargin)
clip: true clip: true
border.color: Appearance.colors.colOutlineVariant
border.width: 1
Behavior on implicitHeight { Behavior on implicitHeight {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this) animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
@@ -488,121 +511,122 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
background: null background: null
onTextChanged: { // Handle suggestions onTextChanged: {
// Handle suggestions
if (messageInputField.text.length === 0) { if (messageInputField.text.length === 0) {
root.suggestionQuery = "" root.suggestionQuery = "";
root.suggestionList = [] root.suggestionList = [];
return return;
} else if (messageInputField.text.startsWith(`${root.commandPrefix}model`)) { } 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 => { const modelResults = Fuzzy.go(root.suggestionQuery, Ai.modelList.map(model => {
return { return {
name: Fuzzy.prepare(model), name: Fuzzy.prepare(model),
obj: model, obj: model
} };
}), { }), {
all: true, all: true,
key: "name" key: "name"
}) });
root.suggestionList = modelResults.map(model => { root.suggestionList = modelResults.map(model => {
return { return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "model ") : ""}${model.target}`, name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "model ") : ""}${model.target}`,
displayName: `${Ai.models[model.target].name}`, 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`)) { } 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 => { const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.promptFiles.map(file => {
return { return {
name: Fuzzy.prepare(file), name: Fuzzy.prepare(file),
obj: file, obj: file
} };
}), { }), {
all: true, all: true,
key: "name" key: "name"
}) });
root.suggestionList = promptFileResults.map(file => { root.suggestionList = promptFileResults.map(file => {
return { return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "prompt ") : ""}${file.target}`, name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "prompt ") : ""}${file.target}`,
displayName: `${FileUtils.trimFileExt(FileUtils.fileNameForPath(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`)) { } 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 => { const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => {
return { return {
name: Fuzzy.prepare(file), name: Fuzzy.prepare(file),
obj: file, obj: file
} };
}), { }), {
all: true, all: true,
key: "name" key: "name"
}) });
root.suggestionList = promptFileResults.map(file => { root.suggestionList = promptFileResults.map(file => {
const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim() const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim();
return { return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "save ") : ""}${chatName}`, name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "save ") : ""}${chatName}`,
displayName: `${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`)) { } 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 => { const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => {
return { return {
name: Fuzzy.prepare(file), name: Fuzzy.prepare(file),
obj: file, obj: file
} };
}), { }), {
all: true, all: true,
key: "name" key: "name"
}) });
root.suggestionList = promptFileResults.map(file => { root.suggestionList = promptFileResults.map(file => {
const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim() const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim();
return { return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "load ") : ""}${chatName}`, name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "load ") : ""}${chatName}`,
displayName: `${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`)) { } 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 => { const toolResults = Fuzzy.go(root.suggestionQuery, Ai.availableTools.map(tool => {
return { return {
name: Fuzzy.prepare(tool), name: Fuzzy.prepare(tool),
obj: tool, obj: tool
} };
}), { }), {
all: true, all: true,
key: "name" key: "name"
}) });
root.suggestionList = toolResults.map(tool => { root.suggestionList = toolResults.map(tool => {
const toolName = tool.target const toolName = tool.target;
return { return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "tool ") : ""}${tool.target}`, name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "tool ") : ""}${tool.target}`,
displayName: toolName, displayName: toolName,
description: Ai.toolDescriptions[toolName], description: Ai.toolDescriptions[toolName]
} };
}) });
} else if(messageInputField.text.startsWith(root.commandPrefix)) { } else if (messageInputField.text.startsWith(root.commandPrefix)) {
root.suggestionQuery = messageInputField.text root.suggestionQuery = messageInputField.text;
root.suggestionList = root.allCommands.filter(cmd => cmd.name.startsWith(messageInputField.text.substring(1))).map(cmd => { root.suggestionList = root.allCommands.filter(cmd => cmd.name.startsWith(messageInputField.text.substring(1))).map(cmd => {
return { return {
name: `${root.commandPrefix}${cmd.name}`, name: `${root.commandPrefix}${cmd.name}`,
description: `${cmd.description}`, description: `${cmd.description}`
} };
}) });
} }
} }
function accept() { function accept() {
root.handleInput(text) root.handleInput(text);
text = "" text = "";
} }
Keys.onPressed: (event) => { Keys.onPressed: event => {
if (event.key === Qt.Key_Tab) { if (event.key === Qt.Key_Tab) {
suggestions.acceptSelectedWord(); suggestions.acceptSelectedWord();
event.accepted = true; 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)) { } else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) {
if (event.modifiers & Qt.ShiftModifier) { if (event.modifiers & Qt.ShiftModifier) {
// Insert newline // Insert newline
messageInputField.insert(messageInputField.cursorPosition, "\n") messageInputField.insert(messageInputField.cursorPosition, "\n");
event.accepted = true event.accepted = true;
} else { // Accept text } else {
const inputText = messageInputField.text // Accept text
messageInputField.clear() const inputText = messageInputField.text;
root.handleInput(inputText) messageInputField.clear();
event.accepted = true 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 } else if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_V) {
if (event.modifiers & Qt.ShiftModifier) { // Let Shift+Ctrl+V = plain paste // Intercept Ctrl+V to handle image/file pasting
messageInputField.text += Quickshell.clipboardText if (event.modifiers & Qt.ShiftModifier) {
// Let Shift+Ctrl+V = plain paste
messageInputField.text += Quickshell.clipboardText;
event.accepted = true; event.accepted = true;
return; return;
} }
// Try image paste first // Try image paste first
const currentClipboardEntry = Cliphist.entries[0] const currentClipboardEntry = Cliphist.entries[0];
const cleanCliphistEntry = StringUtils.cleanCliphistEntry(currentClipboardEntry) const cleanCliphistEntry = StringUtils.cleanCliphistEntry(currentClipboardEntry);
if (/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(currentClipboardEntry)) { // First entry = currently copied entry = image? if (/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(currentClipboardEntry)) {
decodeImageAndAttachProc.handleEntry(currentClipboardEntry) // First entry = currently copied entry = image?
decodeImageAndAttachProc.handleEntry(currentClipboardEntry);
event.accepted = true; event.accepted = true;
return; return;
} else if (cleanCliphistEntry.startsWith("file://")) { // First entry = currently copied entry = image? } else if (cleanCliphistEntry.startsWith("file://")) {
const fileName = decodeURIComponent(cleanCliphistEntry) // First entry = currently copied entry = image?
const fileName = decodeURIComponent(cleanCliphistEntry);
Ai.attachFile(fileName); Ai.attachFile(fileName);
event.accepted = true; event.accepted = true;
return; return;
} }
event.accepted = false; // No image, let text pasting proceed 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) { if (Ai.pendingFilePath.length > 0) {
Ai.attachFile(""); Ai.attachFile("");
event.accepted = true; event.accepted = true;
@@ -668,19 +698,18 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
anchors.fill: parent anchors.fill: parent
cursorShape: sendButton.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: sendButton.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: { onClicked: {
const inputText = messageInputField.text const inputText = messageInputField.text;
root.handleInput(inputText) root.handleInput(inputText);
messageInputField.clear() messageInputField.clear();
} }
} }
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
anchors.centerIn: parent anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
iconSize: Appearance.font.pixelSize.larger iconSize: 22
// fill: sendButton.enabled ? 1 : 0
color: sendButton.enabled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2Disabled 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: "", name: "",
sendDirectly: false, sendDirectly: false,
dontAddSpace: true, dontAddSpace: true
}, },
{ {
name: "clear", name: "clear",
sendDirectly: true, sendDirectly: true
}, },
] ]
ApiInputBoxIndicator { // Model indicator ApiInputBoxIndicator {
// Model indicator
icon: "api" icon: "api"
text: Ai.getModel().name text: Ai.getModel().name
tooltipText: Translation.tr("Current model: %1\nSet it with %2model MODEL") tooltipText: Translation.tr("Current model: %1\nSet it with %2model MODEL").arg(Ai.getModel().name).arg(root.commandPrefix)
.arg(Ai.getModel().name)
.arg(root.commandPrefix)
} }
ApiInputBoxIndicator { // Tool indicator ApiInputBoxIndicator {
// Tool indicator
icon: "service_toolbox" icon: "service_toolbox"
text: Ai.currentTool.charAt(0).toUpperCase() + Ai.currentTool.slice(1) text: Ai.currentTool.charAt(0).toUpperCase() + Ai.currentTool.slice(1)
tooltipText: Translation.tr("Current tool: %1\nSet it with %2tool TOOL") tooltipText: Translation.tr("Current tool: %1\nSet it with %2tool TOOL").arg(Ai.currentTool).arg(root.commandPrefix)
.arg(Ai.currentTool)
.arg(root.commandPrefix)
} }
Item { Layout.fillWidth: true } Item {
Layout.fillWidth: true
}
ButtonGroup { // Command buttons ButtonGroup {
// Command buttons
padding: 0 padding: 0
Repeater { // Command buttons Repeater {
// Command buttons
model: commandButtonsRow.commandsShown model: commandButtonsRow.commandsShown
delegate: ApiCommandButton { delegate: ApiCommandButton {
property string commandRepresentation: `${root.commandPrefix}${modelData.name}` property string commandRepresentation: `${root.commandPrefix}${modelData.name}`
buttonText: commandRepresentation buttonText: commandRepresentation
downAction: () => { downAction: () => {
if (modelData.sendDirectly) { if (modelData.sendDirectly) {
root.handleInput(commandRepresentation) root.handleInput(commandRepresentation);
} else { } else {
messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " ") messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " ");
messageInputField.cursorPosition = messageInputField.text.length messageInputField.cursorPosition = messageInputField.text.length;
messageInputField.forceActiveFocus() messageInputField.forceActiveFocus();
} }
if (modelData.name === "clear") { if (modelData.name === "clear") {
messageInputField.text = "" messageInputField.text = "";
} }
} }
} }
} }
} }
} }
} }
} }
} }
@@ -12,6 +12,8 @@ import Quickshell
Item { Item {
id: root id: root
property real padding: 4
property var inputField: tagInputField property var inputField: tagInputField
readonly property var responses: Booru.responses readonly property var responses: Booru.responses
property string previewDownloadPath: Directories.booruPreviews property string previewDownloadPath: Directories.booruPreviews
@@ -141,7 +143,11 @@ Item {
ColumnLayout { ColumnLayout {
id: columnLayout id: columnLayout
anchors.fill: parent anchors {
fill: parent
margins: root.padding
}
spacing: root.padding
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
@@ -317,14 +323,12 @@ Item {
id: tagInputContainer id: tagInputContainer
property real columnSpacing: 5 property real columnSpacing: 5
Layout.fillWidth: true Layout.fillWidth: true
radius: Appearance.rounding.small radius: Appearance.rounding.normal - root.padding
color: Appearance.colors.colLayer1 color: Appearance.colors.colLayer2
implicitWidth: tagInputField.implicitWidth implicitWidth: tagInputField.implicitWidth
implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin
+ commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + columnSpacing, 45) + commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + columnSpacing, 45)
clip: true clip: true
border.color: Appearance.colors.colOutlineVariant
border.width: 1
Behavior on implicitHeight { Behavior on implicitHeight {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this) animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
@@ -456,10 +460,9 @@ Item {
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
anchors.centerIn: parent anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
iconSize: Appearance.font.pixelSize.larger iconSize: 22
// fill: sendButton.enabled ? 1 : 0
color: sendButton.enabled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2Disabled color: sendButton.enabled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2Disabled
text: "send" text: "arrow_upward"
} }
} }
} }
@@ -127,6 +127,7 @@ Scope { // Scope
sourceComponent: FloatingWindow { sourceComponent: FloatingWindow {
id: detachedSidebarRoot id: detachedSidebarRoot
property var contentParent: detachedSidebarBackground property var contentParent: detachedSidebarBackground
color: "transparent"
visible: GlobalStates.sidebarLeftOpen visible: GlobalStates.sidebarLeftOpen
onVisibleChanged: { onVisibleChanged: {
@@ -21,7 +21,6 @@ Item {
...(root.translatorEnabled ? [{"icon": "translate", "name": Translation.tr("Translator")}] : []), ...(root.translatorEnabled ? [{"icon": "translate", "name": Translation.tr("Translator")}] : []),
...((root.animeEnabled && !root.animeCloset) ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : []) ...((root.animeEnabled && !root.animeCloset) ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : [])
] ]
property int selectedTab: 0
property int tabCount: swipeView.count property int tabCount: swipeView.count
function focusActiveItem() { function focusActiveItem() {
@@ -31,67 +30,64 @@ Item {
Keys.onPressed: (event) => { Keys.onPressed: (event) => {
if (event.modifiers === Qt.ControlModifier) { if (event.modifiers === Qt.ControlModifier) {
if (event.key === Qt.Key_PageDown) { if (event.key === Qt.Key_PageDown) {
root.selectedTab = Math.min(root.selectedTab + 1, root.tabCount - 1) swipeView.incrementCurrentIndex()
event.accepted = true; event.accepted = true;
} }
else if (event.key === Qt.Key_PageUp) { else if (event.key === Qt.Key_PageUp) {
root.selectedTab = Math.max(root.selectedTab - 1, 0) swipeView.decrementCurrentIndex()
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;
event.accepted = true; event.accepted = true;
} }
} }
} }
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors {
anchors.margins: sidebarPadding fill: parent
margins: sidebarPadding
}
spacing: sidebarPadding spacing: sidebarPadding
PrimaryTabBar { // Tab strip Toolbar {
id: tabBar Layout.alignment: Qt.AlignHCenter
visible: root.tabButtonList.length > 1 enableShadow: false
tabButtonList: root.tabButtonList ToolbarTabBar {
Synchronizer on currentIndex { id: tabBar
property alias source: root.selectedTab Layout.alignment: Qt.AlignHCenter
tabButtonList: root.tabButtonList
currentIndex: swipeView.currentIndex
} }
} }
SwipeView { // Content pages Rectangle {
id: swipeView
Layout.topMargin: 5
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
spacing: 10 implicitWidth: swipeView.implicitWidth
implicitHeight: swipeView.implicitHeight
radius: Appearance.rounding.normal
color: Appearance.colors.colLayer1
currentIndex: root.selectedTab SwipeView { // Content pages
onCurrentIndexChanged: { id: swipeView
tabBar.enableIndicatorAnimation = true anchors.fill: parent
root.selectedTab = currentIndex spacing: 10
} currentIndex: tabBar.currentIndex
clip: true clip: true
layer.enabled: true layer.enabled: true
layer.effect: OpacityMask { layer.effect: OpacityMask {
maskSource: Rectangle { maskSource: Rectangle {
width: swipeView.width width: swipeView.width
height: swipeView.height height: swipeView.height
radius: Appearance.rounding.small radius: Appearance.rounding.small
}
} }
}
contentChildren: [ contentChildren: [
...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []), ...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []),
...(root.translatorEnabled ? [translator.createObject()] : []), ...(root.translatorEnabled ? [translator.createObject()] : []),
...(root.animeEnabled ? [anime.createObject()] : []) ...(root.animeEnabled ? [anime.createObject()] : [])
] ]
}
} }
Component { Component {
@@ -13,17 +13,24 @@ import Quickshell.Io
*/ */
Item { Item {
id: root id: root
// Sizes
property real padding: 4
// Widgets // Widgets
property var inputField: inputCanvas.inputTextArea property var inputField: inputCanvas.inputTextArea
// Widget variables // Widget variables
property bool translationFor: false // Indicates if the translation is for an autocorrected text property bool translationFor: false // Indicates if the translation is for an autocorrected text
property string translatedText: "" property string translatedText: ""
property list<string> languages: [] property list<string> languages: []
// Options // Options
property string targetLanguage: Config.options.language.translator.targetLanguage property string targetLanguage: Config.options.language.translator.targetLanguage
property string sourceLanguage: Config.options.language.translator.sourceLanguage property string sourceLanguage: Config.options.language.translator.sourceLanguage
property string hostLanguage: targetLanguage property string hostLanguage: targetLanguage
// States
property bool showLanguageSelector: false property bool showLanguageSelector: false
property bool languageSelectorTarget: false // true for target language, false for source language property bool languageSelectorTarget: false // true for target language, false for source language
@@ -99,7 +106,11 @@ Item {
} }
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors {
fill: parent
margins: root.padding
}
StyledFlickable { StyledFlickable {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
@@ -17,10 +17,8 @@ Rectangle {
default property alias actionButtons: actions.data default property alias actionButtons: actions.data
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: Math.max(150, inputColumn.implicitHeight) implicitHeight: Math.max(150, inputColumn.implicitHeight)
color: isInput ? Appearance.colors.colLayer1 : Appearance.colors.colSurfaceContainer color: Appearance.colors.colLayer2
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
border.color: isInput ? Appearance.colors.colOutlineVariant : "transparent"
border.width: isInput ? 1 : 0
signal inputTextChanged(); // Signal emitted when text changes signal inputTextChanged(); // Signal emitted when text changes
@@ -102,7 +102,7 @@ WindowDialog {
right: parent.right right: parent.right
} }
iconSize: Appearance.font.pixelSize.larger iconSize: Appearance.font.pixelSize.larger
buttonIcon: "destruction" buttonIcon: "flash_off"
text: Translation.tr("Enable") text: Translation.tr("Enable")
checked: Config.options.light.antiFlashbang.enable checked: Config.options.light.antiFlashbang.enable
onCheckedChanged: { onCheckedChanged: {
@@ -29,7 +29,7 @@ AbstractQuickPanel {
readonly property real baseCellHeight: 56 readonly property real baseCellHeight: 56
// Toggles // Toggles
readonly property list<string> availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile","musicRecognition"] readonly property list<string> 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 int columns: Config.options.sidebar.quickToggles.android.columns
readonly property list<var> toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : [] readonly property list<var> toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : []
readonly property list<var> toggleRows: toggleRowsForList(toggles) readonly property list<var> toggleRows: toggleRowsForList(toggles)
@@ -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")
}
}
@@ -12,7 +12,7 @@ AndroidQuickToggleButton {
statusText: toggled ? Translation.tr("Unmuted") : Translation.tr("Muted") statusText: toggled ? Translation.tr("Unmuted") : Translation.tr("Muted")
toggled: !Audio.sink?.audio?.muted toggled: !Audio.sink?.audio?.muted
buttonIcon: Audio.sink?.audio?.muted ? "volume_off" : "volume_up" buttonIcon: Audio.sink?.audio?.muted ? "volume_off" : "volume_up"
onClicked: { mainAction: () => {
Audio.sink.audio.muted = !Audio.sink.audio.muted Audio.sink.audio.muted = !Audio.sink.audio.muted
} }
@@ -14,7 +14,7 @@ AndroidQuickToggleButton {
toggled: BluetoothStatus.enabled toggled: BluetoothStatus.enabled
buttonIcon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled" buttonIcon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
onClicked: { mainAction: () => {
Bluetooth.defaultAdapter.enabled = !Bluetooth.defaultAdapter?.enabled Bluetooth.defaultAdapter.enabled = !Bluetooth.defaultAdapter?.enabled
} }
altAction: () => { altAction: () => {
@@ -13,7 +13,7 @@ AndroidQuickToggleButton {
toggled: false toggled: false
buttonIcon: "cloud_lock" buttonIcon: "cloud_lock"
onClicked: { mainAction: () => {
if (toggled) { if (toggled) {
root.toggled = false root.toggled = false
Quickshell.execDetached(["warp-cli", "disconnect"]) Quickshell.execDetached(["warp-cli", "disconnect"])
@@ -13,7 +13,7 @@ AndroidQuickToggleButton {
toggled: false toggled: false
buttonIcon: "colorize" buttonIcon: "colorize"
onClicked: { mainAction: () => {
GlobalStates.sidebarRightOpen = false; GlobalStates.sidebarRightOpen = false;
delayedActionTimer.start() delayedActionTimer.start()
} }
@@ -13,7 +13,7 @@ AndroidQuickToggleButton {
toggled: Appearance.m3colors.darkmode toggled: Appearance.m3colors.darkmode
buttonIcon: "contrast" buttonIcon: "contrast"
onClicked: event => { mainAction: () => {
if (Appearance.m3colors.darkmode) { if (Appearance.m3colors.darkmode) {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]); Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]);
} else { } else {
@@ -16,7 +16,7 @@ AndroidQuickToggleButton {
EasyEffects.fetchActiveState() EasyEffects.fetchActiveState()
} }
onClicked: { mainAction: () => {
EasyEffects.toggle() EasyEffects.toggle()
} }
@@ -13,7 +13,7 @@ AndroidQuickToggleButton {
toggled: toggled toggled: toggled
buttonIcon: "gamepad" buttonIcon: "gamepad"
onClicked: { mainAction: () => {
root.toggled = !root.toggled root.toggled = !root.toggled
if (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"`]) 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"`])
@@ -11,7 +11,7 @@ AndroidQuickToggleButton {
toggled: Idle.inhibit toggled: Idle.inhibit
buttonIcon: "coffee" buttonIcon: "coffee"
onClicked: { mainAction: () => {
Idle.toggleInhibit() Idle.toggleInhibit()
} }
StyledToolTip { StyledToolTip {
@@ -12,7 +12,7 @@ AndroidQuickToggleButton {
statusText: toggled ? Translation.tr("Enabled") : Translation.tr("Muted") statusText: toggled ? Translation.tr("Enabled") : Translation.tr("Muted")
toggled: !Audio.source?.audio?.muted toggled: !Audio.source?.audio?.muted
buttonIcon: Audio.source?.audio?.muted ? "mic_off" : "mic" buttonIcon: Audio.source?.audio?.muted ? "mic_off" : "mic"
onClicked: { mainAction: () => {
Audio.source.audio.muted = !Audio.source.audio.muted Audio.source.audio.muted = !Audio.source.audio.muted
} }
@@ -21,7 +21,7 @@ AndroidQuickToggleButton {
text: Translation.tr("Recognize music | Right-click to toggle source") text: Translation.tr("Recognize music | Right-click to toggle source")
} }
onClicked: { mainAction: () => {
SongRec.toggleRunning() SongRec.toggleRunning()
} }
@@ -12,7 +12,7 @@ AndroidQuickToggleButton {
toggled: Network.wifiStatus !== "disabled" toggled: Network.wifiStatus !== "disabled"
buttonIcon: Network.materialSymbol buttonIcon: Network.materialSymbol
onClicked: Network.toggleWifi() mainAction: () => Network.toggleWifi()
altAction: () => { altAction: () => {
root.openMenu() root.openMenu()
} }
@@ -14,7 +14,8 @@ AndroidQuickToggleButton {
toggled: Hyprsunset.active toggled: Hyprsunset.active
buttonIcon: auto ? "night_sight_auto" : "bedtime" buttonIcon: auto ? "night_sight_auto" : "bedtime"
onClicked: {
mainAction: () => {
Hyprsunset.toggle() Hyprsunset.toggle()
} }
@@ -13,7 +13,7 @@ AndroidQuickToggleButton {
toggled: !Notifications.silent toggled: !Notifications.silent
buttonIcon: toggled ? "notifications_active" : "notifications_paused" buttonIcon: toggled ? "notifications_active" : "notifications_paused"
onClicked: { mainAction: () => {
Notifications.silent = !Notifications.silent; Notifications.silent = !Notifications.silent;
} }
@@ -11,7 +11,8 @@ AndroidQuickToggleButton {
name: Translation.tr("Virtual Keyboard") name: Translation.tr("Virtual Keyboard")
toggled: GlobalStates.oskOpen toggled: GlobalStates.oskOpen
buttonIcon: toggled ? "keyboard_hide" : "keyboard" buttonIcon: toggled ? "keyboard_hide" : "keyboard"
onClicked: {
mainAction: () => {
GlobalStates.oskOpen = !GlobalStates.oskOpen GlobalStates.oskOpen = !GlobalStates.oskOpen
} }
@@ -22,7 +22,7 @@ AndroidQuickToggleButton {
case PowerProfile.Performance: return "Performance" case PowerProfile.Performance: return "Performance"
} }
onClicked: (event) => { mainAction: () => {
if (PowerProfiles.hasPerformanceProfile) { if (PowerProfiles.hasPerformanceProfile) {
switch(PowerProfiles.profile) { switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: PowerProfiles.profile = PowerProfile.Balanced case PowerProfile.PowerSaver: PowerProfiles.profile = PowerProfile.Balanced
@@ -13,6 +13,7 @@ GroupButton {
required property bool expandedSize required property bool expandedSize
required property string buttonIcon required property string buttonIcon
required property string name required property string name
required property var mainAction
property string statusText: toggled ? Translation.tr("Active") : Translation.tr("Inactive") property string statusText: toggled ? Translation.tr("Active") : Translation.tr("Inactive")
required property real baseCellWidth required property real baseCellWidth
@@ -54,6 +55,11 @@ GroupButton {
property color colText: (toggled && !(altAction && expandedSize)) ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer2 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 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 { contentItem: RowLayout {
id: contentItem id: contentItem
spacing: 4 spacing: 4
@@ -64,35 +70,63 @@ GroupButton {
rightMargin: root.horizontalPadding rightMargin: root.horizontalPadding
} }
Rectangle { // Icon
MouseArea {
id: iconMouseArea
hoverEnabled: true
acceptedButtons: (root.expandedSize && root.altAction) ? Qt.LeftButton : Qt.NoButton
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true Layout.fillHeight: true
Layout.topMargin: root.verticalPadding Layout.topMargin: root.verticalPadding
Layout.bottomMargin: root.verticalPadding Layout.bottomMargin: root.verticalPadding
implicitWidth: height implicitHeight: iconBackground.implicitHeight
radius: root.radius - root.verticalPadding implicitWidth: iconBackground.implicitWidth
color: { cursorShape: Qt.PointingHandCursor
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 { onClicked: root.mainAction()
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
}
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
MaterialSymbol { Rectangle {
anchors.centerIn: parent id: iconBackground
fill: root.toggled ? 1 : 0 anchors.fill: parent
iconSize: root.expandedSize ? 22 : 24 implicitWidth: height
color: root.colIcon radius: root.radius - root.verticalPadding
text: root.buttonIcon 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 { Loader {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true Layout.fillWidth: true
@@ -119,8 +153,9 @@ GroupButton {
} }
font { font {
pixelSize: Appearance.font.pixelSize.smaller pixelSize: Appearance.font.pixelSize.smaller
weight: 100
} }
color: Appearance.colors.colSubtext color: root.colText
elide: Text.ElideRight elide: Text.ElideRight
text: root.statusText text: root.statusText
} }
@@ -14,7 +14,7 @@ AndroidQuickToggleButton {
toggled: false toggled: false
buttonIcon: "screenshot_region" buttonIcon: "screenshot_region"
onClicked: { mainAction: () => {
GlobalStates.sidebarRightOpen = false; GlobalStates.sidebarRightOpen = false;
delayedActionTimer.start() delayedActionTimer.start()
} }
@@ -245,4 +245,20 @@ DelegateChooser {
cellSize: modelData.size 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()
}
} }
} }
@@ -186,6 +186,8 @@ MouseArea {
colBackgroundToggled: Appearance.colors.colSecondaryContainer colBackgroundToggled: Appearance.colors.colSecondaryContainer
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
colRippleToggled: Appearance.colors.colSecondaryContainerActive colRippleToggled: Appearance.colors.colSecondaryContainerActive
buttonRadius: height / 2
implicitHeight: 38
contentItem: RowLayout { contentItem: RowLayout {
MaterialSymbol { MaterialSymbol {
@@ -30,12 +30,7 @@ def image_colorfulness(image):
# scheme-content respects the image's colors very well, but it might # 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 # look too saturated, so we only use it for not very colorful images to be safe
def pick_scheme(colorfulness): def pick_scheme(colorfulness):
if colorfulness < 10: if colorfulness < 40:
# return "scheme-monochrome"
return "scheme-content"
elif colorfulness < 20:
return "scheme-content"
elif colorfulness < 50:
return "scheme-neutral" return "scheme-neutral"
else: else:
return "scheme-tonal-spot" return "scheme-tonal-spot"
+11
View File
@@ -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
+15
View File
@@ -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"
@@ -1,12 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Based on https://unix.stackexchange.com/a/602935 # Based on https://unix.stackexchange.com/a/602935
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Skip if already unlocked # Skip if already unlocked
locked_state=$(busctl --user get-property org.freedesktop.secrets \ if "${SCRIPT_DIR}/is_unlocked.sh"; then
/org/freedesktop/secrets/collection/login \
org.freedesktop.Secret.Collection Locked)
if [[ "${locked_state}" == "b false" ]]; then
echo 'Keyring is already unlocked.' >&2
exit 1 exit 1
fi fi
+4 -2
View File
@@ -532,8 +532,6 @@ Singleton {
modelId = modelId.toLowerCase() modelId = modelId.toLowerCase()
if (modelList.indexOf(modelId) !== -1) { if (modelList.indexOf(modelId) !== -1) {
const model = models[modelId] const model = models[modelId]
// Fetch API keys if needed
if (model?.requires_key) KeyringStorage.fetchKeyringData();
// See if policy prevents online models // See if policy prevents online models
if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) { if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) {
root.addMessage( root.addMessage(
@@ -641,6 +639,10 @@ Singleton {
function makeRequest() { function makeRequest() {
const model = models[currentModelId]; const model = models[currentModelId];
// Fetch API keys if needed
if (model?.requires_key && !KeyringStorage.loaded) KeyringStorage.fetchKeyringData();
requester.currentStrategy = root.currentApiStrategy; requester.currentStrategy = root.currentApiStrategy;
requester.currentStrategy.reset(); // Reset strategy state requester.currentStrategy.reset(); // Reset strategy state
@@ -86,6 +86,10 @@ Singleton {
return str.toLowerCase().replace(/\s+/g, "-"); return str.toLowerCase().replace(/\s+/g, "-");
} }
function getUndescoreToKebabAppName(str) {
return str.toLowerCase().replace(/_/g, "-");
}
function guessIcon(str) { function guessIcon(str) {
if (!str || str.length == 0) return "image-missing"; if (!str || str.length == 0) return "image-missing";
@@ -124,6 +128,8 @@ Singleton {
const kebabNormalizedGuess = getKebabNormalizedAppName(str); const kebabNormalizedGuess = getKebabNormalizedAppName(str);
if (iconExists(kebabNormalizedGuess)) return kebabNormalizedGuess; if (iconExists(kebabNormalizedGuess)) return kebabNormalizedGuess;
const undescoreToKebabGuess = getUndescoreToKebabAppName(str);
if (iconExists(undescoreToKebabGuess)) return undescoreToKebabGuess;
// Search in desktop entries // Search in desktop entries
const iconSearchResults = Fuzzy.go(str, preppedIcons, { const iconSearchResults = Fuzzy.go(str, preppedIcons, {
@@ -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) => { "tagMapFunc": (response) => {
return response.map(item => { return response.map(item => {
return { 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) => { "tagMapFunc": (response) => {
return response.map(item => { return response.map(item => {
return { 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) => { "tagMapFunc": (response) => {
return response.map(item => { return response.map(item => {
return { return {
@@ -151,7 +151,6 @@ Singleton {
} }
}) })
} }
}, },
"gelbooru": { "gelbooru": {
"name": "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) => { "tagMapFunc": (response) => {
return response.tag.map(item => { return response.tag.map(item => {
return { return {
@@ -86,7 +86,7 @@ Singleton {
property int rawMaxBrightness: 100 property int rawMaxBrightness: 100
property real brightness property real brightness
property real brightnessMultiplier: 1.0 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 ready: false
property bool animateChanges: !monitor.isDdc property bool animateChanges: !monitor.isDdc
@@ -1,6 +1,7 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs
import qs.modules.common import qs.modules.common
import qs.modules.common.functions import qs.modules.common.functions
import Quickshell; import Quickshell;
@@ -89,11 +90,13 @@ Singleton {
Process { Process {
id: getData id: getData
command: [ // We need to use echo for a newline so splitparser does parse 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 { stdout: StdioCollector {
onRead: data => { id: keyringDataOutputCollector
if(data.length === 0) return; onStreamFinished: {
const data = keyringDataOutputCollector.text;
if (data.length === 0 || !data.startsWith("{")) return;
try { try {
root.keyringData = JSON.parse(data); root.keyringData = JSON.parse(data);
// console.log("[KeyringStorage] Keyring data fetched:", JSON.stringify(root.keyringData)); // console.log("[KeyringStorage] Keyring data fetched:", JSON.stringify(root.keyringData));
@@ -105,13 +108,15 @@ Singleton {
} }
} }
onExited: (exitCode, exitStatus) => { onExited: (exitCode, exitStatus) => {
// console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode); console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode);
if (exitCode !== 0) { if (exitCode === 1) {
console.error("[KeyringStorage] Failed to get keyring data, reinitializing."); console.error("[KeyringStorage] Entry not found, initializing.");
root.keyringData = {}; root.keyringData = {};
saveKeyringData() saveKeyringData()
} }
root.loaded = true; if (exitCode !== 2) {
root.loaded = true;
}
} }
} }
@@ -5,11 +5,11 @@ pkgdesc='Illogical Impulse Audio Dependencies'
arch=(any) arch=(any)
license=(None) license=(None)
depends=( depends=(
cava cava
pavucontrol-qt pavucontrol-qt
wireplumber wireplumber
pipewire-pulse pipewire-pulse
libdbusmenu-gtk3 libdbusmenu-gtk3
playerctl playerctl
) )
@@ -5,8 +5,8 @@ pkgdesc='Illogical Impulse Backlight Dependencies'
arch=(any) arch=(any)
license=(None) license=(None)
depends=( depends=(
geoclue geoclue
brightnessctl brightnessctl
ddcutil ddcutil
) )
@@ -5,7 +5,6 @@ pkgdesc='Illogical Impulse Basic Dependencies'
arch=(any) arch=(any)
license=(None) license=(None)
depends=( depends=(
axel
bc bc
coreutils coreutils
cliphist cliphist
@@ -14,7 +13,6 @@ depends=(
wget wget
ripgrep ripgrep
jq jq
meson
xdg-user-dirs xdg-user-dirs
# Used in install script # Used in install script
rsync rsync
@@ -6,16 +6,10 @@ arch=(any)
license=(None) license=(None)
depends=( depends=(
hypridle hypridle
hyprcursor
hyprland hyprland
hyprland-qtutils
hyprland-qt-support
hyprlang
hyprlock hyprlock
hyprpicker hyprpicker
hyprsunset hyprsunset
hyprutils
hyprwayland-scanner
xdg-desktop-portal-hyprland xdg-desktop-portal-hyprland
wl-clipboard wl-clipboard
) )
@@ -5,11 +5,11 @@ pkgdesc='Illogical Impulse KDE Dependencies'
arch=(any) arch=(any)
license=(None) license=(None)
depends=( depends=(
bluedevil bluedevil
gnome-keyring gnome-keyring
networkmanager networkmanager
plasma-nm plasma-nm
polkit-kde-agent polkit-kde-agent
dolphin dolphin
systemsettings systemsettings
) )
@@ -8,10 +8,10 @@ arch=("x86_64")
url="https://github.com/NanoMichael/${_pkgname}" url="https://github.com/NanoMichael/${_pkgname}"
license=('MIT') license=('MIT')
depends=( depends=(
tinyxml2 tinyxml2
gtkmm3 gtkmm3
gtksourceviewmm gtksourceviewmm
cairomm cairomm
) )
makedepends=("git" "cmake") makedepends=("git" "cmake")
source=("git+${url}.git") source=("git+${url}.git")
@@ -23,9 +23,9 @@ pkgver() {
} }
prepare() { prepare() {
cd $_pkgname cd $_pkgname
sed -i 's/gtksourceviewmm-3.0/gtksourceviewmm-4.0/' CMakeLists.txt sed -i 's/gtksourceviewmm-3.0/gtksourceviewmm-4.0/' CMakeLists.txt
sed -i 's/tinyxml2.so.10/tinyxml2.so.11/' CMakeLists.txt sed -i 's/tinyxml2.so.10/tinyxml2.so.11/' CMakeLists.txt
} }
build() { build() {
@@ -5,9 +5,9 @@ pkgdesc='Illogical Impulse XDG Desktop Portals'
arch=(any) arch=(any)
license=(None) license=(None)
depends=( depends=(
xdg-desktop-portal xdg-desktop-portal
xdg-desktop-portal-kde xdg-desktop-portal-kde
xdg-desktop-portal-gtk xdg-desktop-portal-gtk
xdg-desktop-portal-hyprland xdg-desktop-portal-hyprland
) )
@@ -12,5 +12,4 @@ depends=(
libsoup3 libsoup3
libportal-gtk4 libportal-gtk4
gobject-introspection gobject-introspection
sassc
) )
@@ -44,7 +44,7 @@ _pkgsrc="$_pkgname"
source=("$_pkgsrc::git+$url.git#commit=$_commit" source=("$_pkgsrc::git+$url.git#commit=$_commit"
quickshell-check.hook) quickshell-check.hook)
sha256sums=('SKIP' sha256sums=('SKIP'
'8543e21aeaaa5441b73a679160e7601a957f16c433e8d6bd9257e80bd0e94083') '8543e21aeaaa5441b73a679160e7601a957f16c433e8d6bd9257e80bd0e94083')
pkgver() { pkgver() {
@@ -5,10 +5,10 @@ pkgdesc='Illogical Impulse Screenshot and Recording Dependencies'
arch=(any) arch=(any)
license=(None) license=(None)
depends=( depends=(
hyprshot hyprshot
slurp slurp
swappy swappy
tesseract tesseract
tesseract-data-eng tesseract-data-eng
wf-recorder wf-recorder
) )
@@ -5,23 +5,23 @@ pkgdesc='Illogical Impulse GTK/Qt Dependencies'
arch=(any) arch=(any)
license=(None) license=(None)
depends=( depends=(
kdialog kdialog
qt6-5compat syntax-highlighting
qt6-avif-image-plugin upower
qt6-base wtype
qt6-declarative ydotool
qt6-imageformats qt6-5compat
qt6-multimedia qt6-avif-image-plugin
qt6-positioning qt6-base
qt6-quicktimeline qt6-declarative
qt6-sensors qt6-imageformats
qt6-svg qt6-multimedia
qt6-tools qt6-positioning
qt6-translations qt6-quicktimeline
qt6-virtualkeyboard qt6-sensors
qt6-wayland qt6-svg
syntax-highlighting qt6-tools
upower qt6-translations
wtype qt6-virtualkeyboard
ydotool qt6-wayland
) )
@@ -6,13 +6,11 @@ arch=(any)
license=(None) license=(None)
depends=( depends=(
fuzzel fuzzel
glib2 # for `gsettings` it seems? glib2
imagemagick imagemagick
hypridle hypridle
hyprutils
hyprlock hyprlock
hyprpicker hyprpicker
nm-connection-editor
songrec songrec
translate-shell translate-shell
wlogout wlogout
@@ -2,7 +2,6 @@
### Must be one package per line as it needs to be compared against the explicitly installed list from pacman ### Must be one package per line as it needs to be compared against the explicitly installed list from pacman
illogical-impulse-ags illogical-impulse-ags
archlinux-xdg-menu archlinux-xdg-menu
axel
bc bc
coreutils coreutils
cliphist cliphist
@@ -14,7 +13,6 @@ wget
ripgrep ripgrep
gojq gojq
npm npm
meson
typescript typescript
gjs gjs
xdg-user-dirs xdg-user-dirs
+6 -1
View File
@@ -9,7 +9,12 @@
Note that this script must be idempotent. Note that this script must be idempotent.
TODO: 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=<user>@.host --user to connect to bus of other user)
```
## Attentions ## Attentions
### PAM ### PAM
+14 -418
View File
@@ -1,57 +1,8 @@
{ {
"nodes": { "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": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems_2" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1731533236, "lastModified": 1731533236,
@@ -67,28 +18,6 @@
"type": "github" "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": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -110,269 +39,10 @@
"type": "github" "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": { "nixgl": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1752054764, "lastModified": 1752054764,
@@ -389,22 +59,6 @@
} }
}, },
"nixpkgs": { "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": { "locked": {
"lastModified": 1746378225, "lastModified": 1746378225,
"narHash": "sha256-OeRSuL8PUjIfL3Q0fTbNJD/fmv1R+K2JAOqWJd3Oceg=", "narHash": "sha256-OeRSuL8PUjIfL3Q0fTbNJD/fmv1R+K2JAOqWJd3Oceg=",
@@ -419,7 +73,7 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_3": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1761597516, "lastModified": 1761597516,
"narHash": "sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8+ON/0Yy8+a5vsDU=", "narHash": "sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8+ON/0Yy8+a5vsDU=",
@@ -434,53 +88,36 @@
"type": "indirect" "type": "indirect"
} }
}, },
"pre-commit-hooks": { "quickshell": {
"inputs": { "inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [ "nixpkgs": [
"hyprland",
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1760663237, "lastModified": 1761821581,
"narHash": "sha256-BflA6U4AM1bzuRMR8QqzPXqh8sWVCNDzOdsxXEguJIc=", "narHash": "sha256-nLuc6jA7z+H/6bHPEBSOYPbz7RtvNCZiTKmYItJuBmM=",
"owner": "cachix", "owner": "quickshell-mirror",
"repo": "git-hooks.nix", "repo": "quickshell",
"rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37", "rev": "db1777c20b936a86528c1095cbcb1ebd92801402",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "cachix", "owner": "quickshell-mirror",
"repo": "git-hooks.nix", "repo": "quickshell",
"rev": "db1777c20b936a86528c1095cbcb1ebd92801402",
"type": "github" "type": "github"
} }
}, },
"root": { "root": {
"inputs": { "inputs": {
"home-manager": "home-manager", "home-manager": "home-manager",
"hyprland": "hyprland",
"nixgl": "nixgl", "nixgl": "nixgl",
"nixpkgs": "nixpkgs_3" "nixpkgs": "nixpkgs_2",
"quickshell": "quickshell"
} }
}, },
"systems": { "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": { "locked": {
"lastModified": 1681028828, "lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
@@ -494,47 +131,6 @@
"repo": "default", "repo": "default",
"type": "github" "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", "root": "root",
+9 -5
View File
@@ -9,13 +9,17 @@
url = "github:nix-community/home-manager/release-25.05"; url = "github:nix-community/home-manager/release-25.05";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
hyprland = { #hyprland = {
url = "github:hyprwm/Hyprland"; # url = "github:hyprwm/Hyprland";
}; #};
nixgl.url = "github:nix-community/nixGL"; 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 let
home_attrs = rec { home_attrs = rec {
username = import ./username.nix; username = import ./username.nix;
@@ -31,7 +35,7 @@
homeConfigurations = { homeConfigurations = {
illogical_impulse = home-manager.lib.homeManagerConfiguration { illogical_impulse = home-manager.lib.homeManagerConfiguration {
inherit pkgs; inherit pkgs;
extraSpecialArgs = { inherit nixgl home_attrs; }; extraSpecialArgs = { inherit home_attrs nixgl quickshell; };
modules = [ modules = [
./home.nix ./home.nix
]; ];
+162 -24
View File
@@ -1,4 +1,4 @@
{ config, lib, pkgs, nixgl, home_attrs, ... }: { config, lib, pkgs, nixgl, quickshell, home_attrs, ... }:
{ {
programs.home-manager.enable = true; programs.home-manager.enable = true;
nixGL.packages = nixgl.packages; nixGL.packages = nixgl.packages;
@@ -10,6 +10,7 @@
xdg-desktop-portal-gnome xdg-desktop-portal-gnome
xdg-desktop-portal-gtk xdg-desktop-portal-gtk
xdg-desktop-portal-wlr xdg-desktop-portal-wlr
kdePackages.xdg-desktop-portal-kde
]; ];
config.hyprland = { config.hyprland = {
default = [ "hyprland" "gtk" ]; default = [ "hyprland" "gtk" ];
@@ -21,7 +22,6 @@
## Allow fontconfig to discover fonts in home.packages ## Allow fontconfig to discover fonts in home.packages
fonts.fontconfig.enable = true; fonts.fontconfig.enable = true;
# home.sessionVariables.NIXOS_OZONE_WL = "1";
wayland.windowManager.hyprland = { wayland.windowManager.hyprland = {
## Make sure home-manager not generate ~/.config/hypr/hyprland.conf ## Make sure home-manager not generate ~/.config/hypr/hyprland.conf
systemd.enable = false; plugins = []; settings = {}; extraConfig = ""; systemd.enable = false; plugins = []; settings = {}; extraConfig = "";
@@ -30,44 +30,182 @@
package = config.lib.nixGL.wrap pkgs.hyprland; package = config.lib.nixGL.wrap pkgs.hyprland;
}; };
home = { home = {
packages = with pkgs; [ packages = with pkgs; [
##### Sure ##### ##### Sure #####
## Basic cli tool ## Basic cli tool
## inetutils: provides hostname, ifconfig, ping, etc. ## inetutils: provides hostname, ifconfig, ping, etc.
## libnotify: provides notify-send ## libnotify: provides notify-send
jq rsync inetutils libnotify inetutils libnotify
## Media related
brightnessctl pavucontrol
## Clipboard/Emoji
wl-clipboard cliphist
## Terminal and shell
foot cowsay lolcat
##### Fonts/Icons/Cursors/Decoration ##### ##### Other MISC #####
fontconfig 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 ##### ##### 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 ### illogical-impulse-audio
libcava #cava libcava #cava (Used in Quickshell config)
lxqt.pavucontrol-qt #pavucontrol-qt lxqt.pavucontrol-qt #pavucontrol-qt (Used in Hyprland and Quickshell config)
wireplumber #wireplumber (not explicitly used) wireplumber #wireplumber (not explicitly used)
pipewire #pipewire-pulse pipewire #pipewire-pulse (not explicitly used)
libdbusmenu-gtk3 #libdbusmenu-gtk3 (not explicitly used) libdbusmenu-gtk3 #libdbusmenu-gtk3 (not explicitly used)
playerctl #playerctl playerctl #playerctl (Used in Hyprland and Quickshell config)
### illogical-impulse-backlight ### 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.override { withDemoAgent = true; }) #geoclue (which demo agent used in Quickshell config)
geoclue2 # geoclue brightnessctl #brightnessctl (Used in Hyprland and Quickshell config)
brightnessctl # brightnessctl ddcutil #ddcutil (Used in Quickshell config)
ddcutil # ddcutil
### 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; }//home_attrs;
} }
+22 -6
View File
@@ -1,6 +1,14 @@
# This script is meant to be sourced. # This script is meant to be sourced.
# It's not for directly running. # 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(){ function install_home-manager(){
# https://nix-community.github.io/home-manager/index.xhtml#sec-install-standalone # https://nix-community.github.io/home-manager/index.xhtml#sec-install-standalone
local cmd=home-manager local cmd=home-manager
@@ -16,6 +24,11 @@ function install_home-manager(){
command -v $cmd && return command -v $cmd && return
echo "Failed in installing $cmd." echo "Failed in installing $cmd."
echo "Please install it by yourself and then retry." 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 return 1
} }
function install_nix(){ function install_nix(){
@@ -48,8 +61,8 @@ function install_curl(){
echo "Please install it by yourself and then retry." echo "Please install it by yourself and then retry."
return 1 return 1
} }
function install_zsh(){ function install_fish(){
local cmd=zsh local cmd=fish
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
x sudo pacman -Syu x sudo pacman -Syu
@@ -99,15 +112,18 @@ function hm_deps(){
################################################## ##################################################
################################################## ##################################################
vianix-warning
if ! command -v curl >/dev/null 2>&1;then if ! command -v curl >/dev/null 2>&1;then
echo -e "${STY_YELLOW}[$0]: \"curl\" not found.${STY_RST}" echo -e "${STY_YELLOW}[$0]: \"curl\" not found.${STY_RST}"
showfun install_curl showfun install_curl
v install_curl v install_curl
fi fi
if ! command -v zsh >/dev/null 2>&1;then if ! command -v fish >/dev/null 2>&1;then
echo -e "${STY_YELLOW}[$0]: \"zsh\" not found.${STY_RST}" echo -e "${STY_YELLOW}[$0]: \"fish\" not found.${STY_RST}"
showfun install_zsh showfun install_fish
v install_zsh v install_fish
fi fi
if ! command -v swaylock >/dev/null 2>&1;then if ! command -v swaylock >/dev/null 2>&1;then
echo -e "${STY_YELLOW}[$0]: \"swaylock\" not found.${STY_RST}" echo -e "${STY_YELLOW}[$0]: \"swaylock\" not found.${STY_RST}"
+7 -18
View File
@@ -5,21 +5,6 @@
# This file is provided for any distros, mainly non-Arch(based) distros. # 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(){ install-Rubik(){
x mkdir -p $REPO_ROOT/cache/Rubik x mkdir -p $REPO_ROOT/cache/Rubik
x cd $REPO_ROOT/cache/Rubik x cd $REPO_ROOT/cache/Rubik
@@ -68,8 +53,8 @@ install-bibata(){
x cd $REPO_ROOT/cache/bibata-cursor x cd $REPO_ROOT/cache/bibata-cursor
name="Bibata-Modern-Classic" name="Bibata-Modern-Classic"
file="$name.tar.xz" file="$name.tar.xz"
# Use axel because `curl -O` always downloads a file with 0 byte size, idk why try rm $file
x axel https://github.com/ful1e5/Bibata_Cursor/releases/latest/download/$file x curl -JLO https://github.com/ful1e5/Bibata_Cursor/releases/latest/download/$file
tar -xf $file tar -xf $file
x sudo mkdir -p /usr/local/share/icons x sudo mkdir -p /usr/local/share/icons
x sudo cp -r $name /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 # 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 uv venv --prompt .venv $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV) -p 3.12
x source $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate 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 x deactivate
} }
+9 -3
View File
@@ -65,9 +65,15 @@ esac
# Helpful link(s): # Helpful link(s):
# http://stackoverflow.com/questions/29581754 # http://stackoverflow.com/questions/29581754
# https://github.com/which-distro/os-release # https://github.com/which-distro/os-release
export OS_RELEASE_FILE=${OS_RELEASE_FILE:-/etc/os-release} OS_RELEASE_FILE_CUSTOM="${REPO_ROOT}/os-release"
test -f ${OS_RELEASE_FILE} || \ if test -f "${OS_RELEASE_FILE_CUSTOM}"; then
( echo "${OS_RELEASE_FILE} does not exist. Aborting..." ; exit 1 ; ) 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=$(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) export OS_DISTRO_ID_LIKE=$(awk -F'=' '/^ID_LIKE=/ { gsub("\"","",$2); print tolower($2) }' ${OS_RELEASE_FILE} 2> /dev/null)
+1
View File
@@ -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: 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: 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: 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: # 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`. # 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`.
+7 -2
View File
@@ -79,13 +79,18 @@ case $SKIP_HYPRLAND in
v cp dots/.config/hypr/hyprland.conf $t v cp dots/.config/hypr/hyprland.conf $t
fi fi
t="$XDG_CONFIG_HOME/hypr/hypridle.conf" 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 if [ -f $t ];then
echo -e "${STY_BLUE}[$0]: \"$t\" already exists.${STY_RST}" 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 existed_hypridle_conf=y
else else
echo -e "${STY_YELLOW}[$0]: \"$t\" does not exist yet.${STY_RST}" 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 existed_hypridle_conf=n
fi fi
t="$XDG_CONFIG_HOME/hypr/hyprlock.conf" t="$XDG_CONFIG_HOME/hypr/hyprlock.conf"
+12
View File
@@ -0,0 +1,12 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = with pkgs; [
pkg-config
meson
ninja
cairo
dbus
dbus-glib
glib
];
}