forked from Shinonome/dots-hyprland
Merge branch 'main' into weather-settings
This commit is contained in:
@@ -445,7 +445,7 @@ Variants {
|
||||
color: bgRoot.colText
|
||||
style: Text.Raised
|
||||
styleColor: Appearance.colors.colShadow
|
||||
animateChange: true
|
||||
animateChange: Config.options.background.clock.digital.animateChange
|
||||
}
|
||||
component ClockStatusText: Row {
|
||||
id: statusTextRow
|
||||
|
||||
@@ -50,9 +50,9 @@ Item {
|
||||
print("[Cookie clock] Setting clock preset for category: " + category)
|
||||
// "abstract", "anime", "city", "minimalist", "landscape", "plants", "person", "space"
|
||||
if (category == "abstract") {
|
||||
applyStyle(10, "dots", "fill", "medium", "dot", "bubble")
|
||||
applyStyle(9, "none", "fill", "medium", "dot", "bubble")
|
||||
} else if (category == "anime") {
|
||||
applyStyle(12, "dots", "fill", "bold", "dot", "bubble")
|
||||
applyStyle(7, "none", "fill", "bold", "dot", "bubble")
|
||||
} else if (category == "city" || category == "space") {
|
||||
applyStyle(23, "full", "hollow", "thin", "classic", "bubble")
|
||||
} else if (category == "minimalist") {
|
||||
@@ -83,121 +83,141 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
property bool useSineCookie: Config.options.background.clock.cookie.useSineCookie
|
||||
DropShadow {
|
||||
source: cookie
|
||||
source: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader
|
||||
anchors.fill: source
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 1
|
||||
radius: 8
|
||||
samples: radius * 2 + 1
|
||||
color: root.colShadow
|
||||
transparentBorder: true
|
||||
|
||||
RotationAnimation on rotation {
|
||||
running: Config.options.background.clock.cookie.constantlyRotate
|
||||
duration: 30000
|
||||
easing.type: Easing.Linear
|
||||
loops: Animation.Infinite
|
||||
from: 360
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
id: sineCookieLoader
|
||||
z: 0
|
||||
visible: false // The DropShadow already draws it
|
||||
active: useSineCookie
|
||||
sourceComponent: SineCookie {
|
||||
implicitSize: root.implicitSize
|
||||
sides: Config.options.background.clock.cookie.sides
|
||||
color: root.colBackground
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
id: roundedPolygonCookieLoader
|
||||
z: 0
|
||||
visible: false // The DropShadow already draws it
|
||||
active: !useSineCookie
|
||||
sourceComponent: MaterialCookie {
|
||||
implicitSize: root.implicitSize
|
||||
sides: Config.options.background.clock.cookie.sides
|
||||
color: root.colBackground
|
||||
}
|
||||
}
|
||||
|
||||
MaterialCookie {
|
||||
id: cookie
|
||||
z: 0
|
||||
implicitSize: root.implicitSize
|
||||
amplitude: implicitSize / 70
|
||||
sides: Config.options.background.clock.cookie.sides
|
||||
color: root.colBackground
|
||||
constantlyRotate: Config.options.background.clock.cookie.constantlyRotate
|
||||
|
||||
// Hour/minutes numbers/dots/lines
|
||||
MinuteMarks {
|
||||
anchors.fill: parent
|
||||
// Hour/minutes numbers/dots/lines
|
||||
MinuteMarks {
|
||||
anchors.fill: parent
|
||||
color: root.colOnBackground
|
||||
}
|
||||
|
||||
// Stupid extra hour marks in the middle
|
||||
FadeLoader {
|
||||
id: hourMarksLoader
|
||||
anchors.centerIn: parent
|
||||
shown: Config.options.background.clock.cookie.hourMarks
|
||||
sourceComponent: HourMarks {
|
||||
implicitSize: 135 * (1.75 - 0.75 * hourMarksLoader.opacity)
|
||||
color: root.colOnBackground
|
||||
colOnBackground: ColorUtils.mix(root.colBackgroundInfo, root.colOnBackground, 0.5)
|
||||
}
|
||||
}
|
||||
|
||||
// Number column in the middle
|
||||
FadeLoader {
|
||||
id: timeColumnLoader
|
||||
anchors.centerIn: parent
|
||||
shown: Config.options.background.clock.cookie.timeIndicators
|
||||
scale: 1.4 - 0.4 * timeColumnLoader.shown
|
||||
Behavior on scale {
|
||||
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
// Stupid extra hour marks in the middle
|
||||
FadeLoader {
|
||||
id: hourMarksLoader
|
||||
anchors.centerIn: parent
|
||||
shown: Config.options.background.clock.cookie.hourMarks
|
||||
sourceComponent: HourMarks {
|
||||
implicitSize: 135 * (1.75 - 0.75 * hourMarksLoader.opacity)
|
||||
color: root.colOnBackground
|
||||
colOnBackground: ColorUtils.mix(root.colBackgroundInfo, root.colOnBackground, 0.5)
|
||||
}
|
||||
sourceComponent: TimeColumn {
|
||||
color: root.colBackgroundInfo
|
||||
}
|
||||
}
|
||||
|
||||
// Number column in the middle
|
||||
FadeLoader {
|
||||
id: timeColumnLoader
|
||||
anchors.centerIn: parent
|
||||
shown: Config.options.background.clock.cookie.timeIndicators
|
||||
scale: 1.4 - 0.4 * timeColumnLoader.shown
|
||||
Behavior on scale {
|
||||
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
sourceComponent: TimeColumn {
|
||||
color: root.colBackgroundInfo
|
||||
}
|
||||
// Hour hand
|
||||
FadeLoader {
|
||||
anchors.fill: parent
|
||||
z: 1
|
||||
shown: Config.options.background.clock.cookie.hourHandStyle !== "hide"
|
||||
sourceComponent: HourHand {
|
||||
clockHour: root.clockHour
|
||||
clockMinute: root.clockMinute
|
||||
style: Config.options.background.clock.cookie.hourHandStyle
|
||||
color: root.colHourHand
|
||||
}
|
||||
}
|
||||
|
||||
// Hour hand
|
||||
FadeLoader {
|
||||
// Minute hand
|
||||
FadeLoader {
|
||||
anchors.fill: parent
|
||||
z: 2
|
||||
shown: Config.options.background.clock.cookie.minuteHandStyle !== "hide"
|
||||
sourceComponent: MinuteHand {
|
||||
anchors.fill: parent
|
||||
z: 1
|
||||
shown: Config.options.background.clock.cookie.hourHandStyle !== "hide"
|
||||
sourceComponent: HourHand {
|
||||
clockHour: root.clockHour
|
||||
clockMinute: root.clockMinute
|
||||
style: Config.options.background.clock.cookie.hourHandStyle
|
||||
color: root.colHourHand
|
||||
}
|
||||
clockMinute: root.clockMinute
|
||||
style: Config.options.background.clock.cookie.minuteHandStyle
|
||||
color: root.colMinuteHand
|
||||
}
|
||||
}
|
||||
|
||||
// Minute hand
|
||||
FadeLoader {
|
||||
anchors.fill: parent
|
||||
z: 2
|
||||
shown: Config.options.background.clock.cookie.minuteHandStyle !== "hide"
|
||||
sourceComponent: MinuteHand {
|
||||
anchors.fill: parent
|
||||
clockMinute: root.clockMinute
|
||||
style: Config.options.background.clock.cookie.minuteHandStyle
|
||||
color: root.colMinuteHand
|
||||
}
|
||||
// Second hand
|
||||
FadeLoader {
|
||||
id: secondHandLoader
|
||||
z: (Config.options.background.clock.cookie.secondHandStyle === "line") ? 2 : 3
|
||||
shown: Config.options.time.secondPrecision && Config.options.background.clock.cookie.secondHandStyle !== "hide"
|
||||
anchors.fill: parent
|
||||
sourceComponent: SecondHand {
|
||||
id: secondHand
|
||||
clockSecond: root.clockSecond
|
||||
style: Config.options.background.clock.cookie.secondHandStyle
|
||||
color: root.colSecondHand
|
||||
}
|
||||
}
|
||||
|
||||
// Second hand
|
||||
FadeLoader {
|
||||
id: secondHandLoader
|
||||
z: (Config.options.background.clock.cookie.secondHandStyle === "line") ? 2 : 3
|
||||
shown: Config.options.time.secondPrecision && Config.options.background.clock.cookie.secondHandStyle !== "hide"
|
||||
anchors.fill: parent
|
||||
sourceComponent: SecondHand {
|
||||
id: secondHand
|
||||
clockSecond: root.clockSecond
|
||||
style: Config.options.background.clock.cookie.secondHandStyle
|
||||
color: root.colSecondHand
|
||||
}
|
||||
// Center dot
|
||||
FadeLoader {
|
||||
z: 4
|
||||
anchors.centerIn: parent
|
||||
shown: Config.options.background.clock.cookie.minuteHandStyle !== "bold"
|
||||
sourceComponent: Rectangle {
|
||||
color: Config.options.background.clock.cookie.minuteHandStyle === "medium" ? root.colBackground : root.colMinuteHand
|
||||
implicitWidth: 6
|
||||
implicitHeight: implicitWidth
|
||||
radius: width / 2
|
||||
}
|
||||
}
|
||||
|
||||
// Center dot
|
||||
FadeLoader {
|
||||
z: 4
|
||||
anchors.centerIn: parent
|
||||
shown: Config.options.background.clock.cookie.minuteHandStyle !== "bold"
|
||||
sourceComponent: Rectangle {
|
||||
color: Config.options.background.clock.cookie.minuteHandStyle === "medium" ? root.colBackground : root.colMinuteHand
|
||||
implicitWidth: 6
|
||||
implicitHeight: implicitWidth
|
||||
radius: width / 2
|
||||
}
|
||||
}
|
||||
// Date
|
||||
FadeLoader {
|
||||
anchors.fill: parent
|
||||
shown: Config.options.background.clock.cookie.dateStyle !== "hide"
|
||||
|
||||
// Date
|
||||
FadeLoader {
|
||||
anchors.fill: parent
|
||||
shown: Config.options.background.clock.cookie.dateStyle !== "hide"
|
||||
|
||||
sourceComponent: DateIndicator {
|
||||
color: root.colBackgroundInfo
|
||||
style: Config.options.background.clock.cookie.dateStyle
|
||||
}
|
||||
sourceComponent: DateIndicator {
|
||||
color: root.colBackgroundInfo
|
||||
style: Config.options.background.clock.cookie.dateStyle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ Item {
|
||||
required property int clockHour
|
||||
required property int clockMinute
|
||||
property real handLength: 72
|
||||
property real handWidth: 18
|
||||
property real handWidth: 20
|
||||
property string style: "fill"
|
||||
property color color: Appearance.colors.colPrimary
|
||||
|
||||
@@ -19,6 +19,14 @@ Item {
|
||||
}
|
||||
|
||||
rotation: -90 + (360 / 12) * (root.clockHour + root.clockMinute / 60)
|
||||
Behavior on rotation {
|
||||
animation: RotationAnimation {
|
||||
direction: RotationAnimation.Clockwise
|
||||
duration: 300
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.animationCurves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
@@ -10,13 +10,17 @@ Item {
|
||||
required property int clockMinute
|
||||
property string style: "medium"
|
||||
property real handLength: 95
|
||||
property real handWidth: style === "bold" ? 18 : style === "medium" ? 12 : 5
|
||||
property real handWidth: style === "bold" ? 20 : style === "medium" ? 12 : 5
|
||||
property color color: Appearance.colors.colSecondary
|
||||
|
||||
rotation: -90 + (360 / 60) * root.clockMinute
|
||||
|
||||
Behavior on rotation {
|
||||
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
|
||||
animation: RotationAnimation {
|
||||
direction: RotationAnimation.Clockwise
|
||||
duration: 300
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.animationCurves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
||||
@@ -10,7 +10,7 @@ Item {
|
||||
|
||||
required property int clockSecond
|
||||
property real handWidth: 2
|
||||
property real handLength: 100
|
||||
property real handLength: 95
|
||||
property real dotSize: 20
|
||||
property string style: "hide"
|
||||
property color color: Appearance.colors.colSecondary
|
||||
@@ -19,7 +19,8 @@ Item {
|
||||
|
||||
Behavior on rotation {
|
||||
enabled: Config.options.background.clock.cookie.constantlyRotate // Animating every second is expensive...
|
||||
animation: NumberAnimation {
|
||||
animation: RotationAnimation {
|
||||
direction: RotationAnimation.Clockwise
|
||||
duration: 1000 // 1 second
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
@@ -29,7 +30,7 @@ Item {
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
leftMargin: 10
|
||||
leftMargin: 10 + (root.style === "dot" ? root.dotSize : 0)
|
||||
}
|
||||
implicitWidth: root.style === "dot" ? root.dotSize : root.handLength
|
||||
implicitHeight: root.style === "dot" ? root.dotSize : root.handWidth
|
||||
|
||||
+8
-2
@@ -14,7 +14,10 @@ Item {
|
||||
// 12 Dots
|
||||
FadeLoader {
|
||||
id: dotsLoader
|
||||
anchors.fill: parent
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 10
|
||||
}
|
||||
shown: root.style === "dots"
|
||||
sourceComponent: Dots {
|
||||
color: root.color
|
||||
@@ -37,7 +40,10 @@ Item {
|
||||
// Lines
|
||||
FadeLoader {
|
||||
id: linesLoader
|
||||
anchors.fill: parent
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 10
|
||||
}
|
||||
shown: root.style === "full"
|
||||
sourceComponent: Lines {
|
||||
color: root.color
|
||||
|
||||
@@ -5,6 +5,7 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Qt.labs.synchronizer
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
@@ -22,7 +23,6 @@ Scope { // Scope
|
||||
"name": Translation.tr("Elements")
|
||||
},
|
||||
]
|
||||
property int selectedTab: 0
|
||||
|
||||
Loader {
|
||||
id: cheatsheetLoader
|
||||
@@ -31,6 +31,7 @@ Scope { // Scope
|
||||
sourceComponent: PanelWindow { // Window
|
||||
id: cheatsheetRoot
|
||||
visible: cheatsheetLoader.active
|
||||
property int selectedTab: 0
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
@@ -85,16 +86,16 @@ Scope { // Scope
|
||||
}
|
||||
if (event.modifiers === Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_PageDown) {
|
||||
root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1);
|
||||
cheatsheetRoot.selectedTab = Math.min(cheatsheetRoot.selectedTab + 1, root.tabButtonList.length - 1);
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_PageUp) {
|
||||
root.selectedTab = Math.max(root.selectedTab - 1, 0);
|
||||
cheatsheetRoot.selectedTab = Math.max(cheatsheetRoot.selectedTab - 1, 0);
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length;
|
||||
cheatsheetRoot.selectedTab = (cheatsheetRoot.selectedTab + 1) % root.tabButtonList.length;
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length;
|
||||
cheatsheetRoot.selectedTab = (cheatsheetRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
@@ -140,9 +141,8 @@ Scope { // Scope
|
||||
PrimaryTabBar { // Tab strip
|
||||
id: tabBar
|
||||
tabButtonList: root.tabButtonList
|
||||
externalTrackedTab: root.selectedTab
|
||||
function onCurrentIndexChanged(currentIndex) {
|
||||
root.selectedTab = currentIndex;
|
||||
Synchronizer on currentIndex {
|
||||
property alias source: cheatsheetRoot.selectedTab
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,12 +164,12 @@ Scope { // Scope
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
currentIndex: tabBar.externalTrackedTab
|
||||
currentIndex: cheatsheetRoot.selectedTab
|
||||
onCurrentIndexChanged: {
|
||||
contentWidthBehavior.enabled = true;
|
||||
contentHeightBehavior.enabled = true;
|
||||
tabBar.enableIndicatorAnimation = true;
|
||||
root.selectedTab = currentIndex;
|
||||
cheatsheetRoot.selectedTab = currentIndex;
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
@@ -164,6 +164,10 @@ Singleton {
|
||||
property bool hourMarks: false
|
||||
property bool dateInClock: true
|
||||
property bool constantlyRotate: false
|
||||
property bool useSineCookie: false
|
||||
}
|
||||
property JsonObject digital: JsonObject {
|
||||
property bool animateChange: true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -324,6 +328,7 @@ Singleton {
|
||||
property bool unlockKeyring: true
|
||||
property bool requirePasswordToPower: false
|
||||
}
|
||||
property bool materialShapeChars: true
|
||||
}
|
||||
|
||||
property JsonObject media: JsonObject {
|
||||
|
||||
@@ -2,7 +2,7 @@ pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.modules.common.functions
|
||||
import Qt.labs.platform
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
|
||||
@@ -2,69 +2,38 @@ import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets.shapes
|
||||
import "shapes/geometry/offset.js" as Offset
|
||||
import "shapes/shapes/corner-rounding.js" as CornerRounding
|
||||
import "shapes/shapes/rounded-polygon.js" as RoundedPolygon
|
||||
import "shapes/material-shapes.js" as MaterialShapes
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real sides: 12
|
||||
property int sides: 12
|
||||
property int implicitSize: 100
|
||||
property real amplitude: implicitSize / 50
|
||||
property int renderPoints: 360
|
||||
property color color: "#605790"
|
||||
property alias strokeWidth: shapePath.strokeWidth
|
||||
property bool constantlyRotate: false
|
||||
property alias color: shapeCanvas.color
|
||||
|
||||
implicitWidth: implicitSize
|
||||
implicitHeight: implicitSize
|
||||
|
||||
property real shapeRotation: 0
|
||||
property var cornerRounding: new CornerRounding.CornerRounding((sides < 17 ? 1.5 : 1.1) / Math.max(sides, 1))
|
||||
|
||||
Loader {
|
||||
active: constantlyRotate
|
||||
sourceComponent: FrameAnimation {
|
||||
running: true
|
||||
onTriggered: {
|
||||
shapeRotation += 0.05
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on sides {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Shape {
|
||||
id: shape
|
||||
ShapeCanvas {
|
||||
id: shapeCanvas
|
||||
anchors.fill: parent
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath {
|
||||
id: shapePath
|
||||
strokeWidth: 0
|
||||
fillColor: root.color
|
||||
pathHints: ShapePath.PathSolid & ShapePath.PathNonIntersecting
|
||||
|
||||
PathPolyline {
|
||||
property var pointsList: {
|
||||
var points = []
|
||||
var cx = shape.width / 2 // center x
|
||||
var cy = shape.height / 2 // center y
|
||||
var steps = root.renderPoints
|
||||
var radius = root.implicitSize / 2 - root.amplitude
|
||||
for (var i = 0; i <= steps; i++) {
|
||||
var angle = (i / steps) * 2 * Math.PI
|
||||
var rotatedAngle = angle * root.sides + Math.PI/2 + (root.shapeRotation * root.constantlyRotate)
|
||||
var wave = Math.sin(rotatedAngle) * root.amplitude
|
||||
var x = Math.cos(angle) * (radius + wave) + cx
|
||||
var y = Math.sin(angle) * (radius + wave) + cy
|
||||
points.push(Qt.point(x, y))
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
path: pointsList
|
||||
}
|
||||
|
||||
roundedPolygon: switch(sides) {
|
||||
case 0: return MaterialShapes.getCircle();
|
||||
case 1: return MaterialShapes.getCircle();
|
||||
case 4: return MaterialShapes.getCookie4Sided();
|
||||
case 6: return MaterialShapes.getCookie6Sided();
|
||||
case 7: return MaterialShapes.getCookie7Sided();
|
||||
case 9: return MaterialShapes.getCookie9Sided();
|
||||
case 12: return MaterialShapes.getCookie12Sided();
|
||||
default: return RoundedPolygon.RoundedPolygon.star(sides, 1, 0.8, root.cornerRounding)
|
||||
.transformed((x, y) => MaterialShapes.rotate30.map(new Offset.Offset(x, y)))
|
||||
.normalized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ ShapeCanvas {
|
||||
property double implicitSize
|
||||
implicitHeight: implicitSize
|
||||
implicitWidth: implicitSize
|
||||
polygonIsNormalized: true
|
||||
roundedPolygon: {
|
||||
switch (root.shape) {
|
||||
case MaterialShape.Shape.Circle: return MaterialShapes.getCircle();
|
||||
|
||||
@@ -3,16 +3,20 @@ 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")}]
|
||||
required property var externalTrackedTab
|
||||
property int currentIndex
|
||||
property bool enableIndicatorAnimation: false
|
||||
property color colIndicator: Appearance?.colors.colPrimary ?? "#65558F"
|
||||
property color colBorder: Appearance?.m3colors.m3outlineVariant ?? "#C6C6D0"
|
||||
signal currentIndexChanged(int index)
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
enableIndicatorAnimation = true
|
||||
}
|
||||
|
||||
property bool centerTabBar: parent.width > 500
|
||||
Layout.fillWidth: !centerTabBar
|
||||
@@ -22,9 +26,8 @@ ColumnLayout {
|
||||
TabBar {
|
||||
id: tabBar
|
||||
Layout.fillWidth: true
|
||||
currentIndex: root.externalTrackedTab
|
||||
onCurrentIndexChanged: {
|
||||
root.onCurrentIndexChanged(currentIndex)
|
||||
Synchronizer on currentIndex {
|
||||
property alias source: root.currentIndex
|
||||
}
|
||||
|
||||
background: Item {
|
||||
@@ -42,10 +45,11 @@ ColumnLayout {
|
||||
Repeater {
|
||||
model: root.tabButtonList
|
||||
delegate: PrimaryTabButton {
|
||||
selected: (index == root.externalTrackedTab)
|
||||
selected: (index == root.currentIndex)
|
||||
buttonText: modelData.name
|
||||
buttonIcon: modelData.icon
|
||||
minimumWidth: 160
|
||||
onClicked: root.currentIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,12 +58,6 @@ ColumnLayout {
|
||||
id: tabIndicator
|
||||
Layout.fillWidth: true
|
||||
height: 3
|
||||
Connections {
|
||||
target: root
|
||||
function onExternalTrackedTabChanged() {
|
||||
root.enableIndicatorAnimation = true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: indicator
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real sides: 12
|
||||
property int implicitSize: 100
|
||||
property real amplitude: implicitSize / 50
|
||||
property int renderPoints: 360
|
||||
property color color: "#605790"
|
||||
property alias strokeWidth: shapePath.strokeWidth
|
||||
property bool constantlyRotate: false
|
||||
|
||||
implicitWidth: implicitSize
|
||||
implicitHeight: implicitSize
|
||||
|
||||
property real shapeRotation: 0
|
||||
|
||||
Loader {
|
||||
active: constantlyRotate
|
||||
sourceComponent: FrameAnimation {
|
||||
running: true
|
||||
onTriggered: {
|
||||
shapeRotation += 0.05
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on sides {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Shape {
|
||||
id: shape
|
||||
anchors.fill: parent
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath {
|
||||
id: shapePath
|
||||
strokeWidth: 0
|
||||
fillColor: root.color
|
||||
pathHints: ShapePath.PathSolid & ShapePath.PathNonIntersecting
|
||||
|
||||
PathPolyline {
|
||||
property var pointsList: {
|
||||
var points = []
|
||||
var cx = shape.width / 2 // center x
|
||||
var cy = shape.height / 2 // center y
|
||||
var steps = root.renderPoints
|
||||
var radius = root.implicitSize / 2 - root.amplitude
|
||||
for (var i = 0; i <= steps; i++) {
|
||||
var angle = (i / steps) * 2 * Math.PI
|
||||
var rotatedAngle = angle * root.sides + Math.PI/2 + (root.shapeRotation * root.constantlyRotate)
|
||||
var wave = Math.sin(rotatedAngle) * root.amplitude
|
||||
var x = Math.cos(angle) * (radius + wave) + cx
|
||||
var y = Math.sin(angle) * (radius + wave) + cy
|
||||
points.push(Qt.point(x, y))
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
path: pointsList
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,13 +45,25 @@ Switch {
|
||||
anchors.leftMargin: root.checked ? ((root.pressed || root.down) ? (22 * root.scale) : 24 * root.scale) : ((root.pressed || root.down) ? (2 * root.scale) : 8 * root.scale)
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
NumberAnimation {
|
||||
duration: Appearance.animationCurves.expressiveFastSpatialDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
Behavior on width {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
NumberAnimation {
|
||||
duration: Appearance.animationCurves.expressiveFastSpatialDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
Behavior on height {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
NumberAnimation {
|
||||
duration: Appearance.animationCurves.expressiveFastSpatialDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
|
||||
Submodule dots/.config/quickshell/ii/modules/common/widgets/shapes updated: 2e9263e011...8aa62a41bd
@@ -1,4 +1,6 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.lock
|
||||
@@ -9,9 +11,9 @@ import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
function unlockKeyring() {
|
||||
function unlockKeyring() {
|
||||
Quickshell.execDetached({
|
||||
environment: ({
|
||||
UNLOCK_PASSWORD: root.currentText
|
||||
@@ -20,117 +22,141 @@ Scope {
|
||||
})
|
||||
}
|
||||
|
||||
// This stores all the information shared between the lock surfaces on each screen.
|
||||
// https://github.com/quickshell-mirror/quickshell-examples/tree/master/lockscreen
|
||||
LockContext {
|
||||
id: lockContext
|
||||
property var windowData: []
|
||||
function saveWindowPositionAndTile() {
|
||||
Hyprland.dispatch(`keyword dwindle:pseudotile true`)
|
||||
root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id))
|
||||
root.windowData.forEach(w => {
|
||||
Hyprland.dispatch(`pseudo address:${w.address}`)
|
||||
Hyprland.dispatch(`settiled address:${w.address}`)
|
||||
Hyprland.dispatch(`movetoworkspacesilent ${w.workspace.id},address:${w.address}`)
|
||||
})
|
||||
}
|
||||
function restoreWindowPositionAndTile() {
|
||||
root.windowData.forEach(w => {
|
||||
Hyprland.dispatch(`setfloating address:${w.address}`)
|
||||
Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`)
|
||||
Hyprland.dispatch(`pseudo address:${w.address}`)
|
||||
})
|
||||
Hyprland.dispatch(`keyword dwindle:pseudotile false`)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onScreenLockedChanged() {
|
||||
if (GlobalStates.screenLocked) lockContext.reset();
|
||||
}
|
||||
}
|
||||
// This stores all the information shared between the lock surfaces on each screen.
|
||||
// https://github.com/quickshell-mirror/quickshell-examples/tree/master/lockscreen
|
||||
LockContext {
|
||||
id: lockContext
|
||||
|
||||
onUnlocked: (targetAction) => {
|
||||
// Perform the target action if it's not just unlocking
|
||||
if (targetAction == LockContext.ActionEnum.Poweroff) {
|
||||
Session.poweroff();
|
||||
return;
|
||||
} else if (targetAction == LockContext.ActionEnum.Reboot) {
|
||||
Session.reboot();
|
||||
return;
|
||||
}
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onScreenLockedChanged() {
|
||||
if (GlobalStates.screenLocked) {
|
||||
lockContext.reset();
|
||||
lockContext.tryFingerUnlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock the keyring if configured to do so
|
||||
if (Config.options.lock.security.unlockKeyring) root.unlockKeyring();
|
||||
onUnlocked: (targetAction) => {
|
||||
// Perform the target action if it's not just unlocking
|
||||
if (targetAction == LockContext.ActionEnum.Poweroff) {
|
||||
Session.poweroff();
|
||||
return;
|
||||
} else if (targetAction == LockContext.ActionEnum.Reboot) {
|
||||
Session.reboot();
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlock the screen before exiting, or the compositor will display a
|
||||
// fallback lock you can't interact with.
|
||||
GlobalStates.screenLocked = false;
|
||||
|
||||
// Refocus last focused window on unlock (hack)
|
||||
Quickshell.execDetached(["bash", "-c", `sleep 0.2; hyprctl --batch "dispatch togglespecialworkspace; dispatch togglespecialworkspace"`])
|
||||
// Unlock the keyring if configured to do so
|
||||
if (Config.options.lock.security.unlockKeyring) root.unlockKeyring();
|
||||
|
||||
// Unlock the screen before exiting, or the compositor will display a
|
||||
// fallback lock you can't interact with.
|
||||
GlobalStates.screenLocked = false;
|
||||
|
||||
// Refocus last focused window on unlock (hack)
|
||||
Quickshell.execDetached(["bash", "-c", `sleep 0.2; hyprctl --batch "dispatch togglespecialworkspace; dispatch togglespecialworkspace"`])
|
||||
|
||||
// Reset
|
||||
lockContext.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
locked: GlobalStates.screenLocked
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
locked: GlobalStates.screenLocked
|
||||
|
||||
WlSessionLockSurface {
|
||||
color: "transparent"
|
||||
Loader {
|
||||
active: GlobalStates.screenLocked
|
||||
anchors.fill: parent
|
||||
opacity: active ? 1 : 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
sourceComponent: LockSurface {
|
||||
context: lockContext
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WlSessionLockSurface {
|
||||
color: "transparent"
|
||||
Loader {
|
||||
active: GlobalStates.screenLocked
|
||||
anchors.fill: parent
|
||||
opacity: active ? 1 : 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
sourceComponent: LockSurface {
|
||||
context: lockContext
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blur layer hack
|
||||
Variants {
|
||||
// Blur layer hack
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
delegate: Scope {
|
||||
required property ShellScreen modelData
|
||||
property bool shouldPush: GlobalStates.screenLocked
|
||||
property string targetMonitorName: modelData.name
|
||||
property int verticalMovementDistance: modelData.height
|
||||
property int horizontalSqueeze: modelData.width * 0.2
|
||||
onShouldPushChanged: {
|
||||
if (shouldPush) {
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, ${verticalMovementDistance}, ${-verticalMovementDistance}, ${horizontalSqueeze}, ${horizontalSqueeze}`])
|
||||
} else {
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, 0, 0, 0, 0`])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: Scope {
|
||||
required property ShellScreen modelData
|
||||
property bool shouldPush: GlobalStates.screenLocked
|
||||
property string targetMonitorName: modelData.name
|
||||
property int verticalMovementDistance: modelData.height
|
||||
property int horizontalSqueeze: modelData.width * 0.2
|
||||
onShouldPushChanged: {
|
||||
if (shouldPush) {
|
||||
root.saveWindowPositionAndTile();
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, ${verticalMovementDistance}, ${-verticalMovementDistance}, ${horizontalSqueeze}, ${horizontalSqueeze}`])
|
||||
} else {
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, 0, 0, 0, 0`])
|
||||
root.restoreWindowPositionAndTile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
IpcHandler {
|
||||
target: "lock"
|
||||
|
||||
function activate(): void {
|
||||
GlobalStates.screenLocked = true;
|
||||
}
|
||||
function focus(): void {
|
||||
lockContext.shouldReFocus();
|
||||
}
|
||||
function focus(): void {
|
||||
lockContext.shouldReFocus();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
GlobalShortcut {
|
||||
name: "lock"
|
||||
description: "Locks the screen"
|
||||
|
||||
onPressed: {
|
||||
if (Config.options.lock.useHyprlock) {
|
||||
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
||||
return;
|
||||
}
|
||||
if (Config.options.lock.useHyprlock) {
|
||||
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
||||
return;
|
||||
}
|
||||
GlobalStates.screenLocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
GlobalShortcut {
|
||||
name: "lockFocus"
|
||||
description: "Re-focuses the lock screen. This is because Hyprland after waking up for whatever reason"
|
||||
+ "decides to keyboard-unfocus the lock screen"
|
||||
+ "decides to keyboard-unfocus the lock screen"
|
||||
|
||||
onPressed: {
|
||||
lockContext.shouldReFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
Connections {
|
||||
target: Config
|
||||
function onReadyChanged() {
|
||||
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import qs
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Pam
|
||||
|
||||
Scope {
|
||||
@@ -18,6 +19,7 @@ Scope {
|
||||
property string currentText: ""
|
||||
property bool unlockInProgress: false
|
||||
property bool showFailure: false
|
||||
property bool fingerprintsConfigured: false
|
||||
property var targetAction: LockContext.ActionEnum.Unlock
|
||||
|
||||
function resetTargetAction() {
|
||||
@@ -60,6 +62,34 @@ Scope {
|
||||
pam.start();
|
||||
}
|
||||
|
||||
function tryFingerUnlock() {
|
||||
if (root.fingerprintsConfigured) {
|
||||
fingerPam.start();
|
||||
}
|
||||
}
|
||||
|
||||
function stopFingerPam() {
|
||||
fingerPam.abort();
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fingerprintCheckProc
|
||||
running: true
|
||||
command: ["bash", "-c", "fprintd-list $(whoami)"]
|
||||
stdout: StdioCollector {
|
||||
id: fingerprintOutputCollector
|
||||
onStreamFinished: {
|
||||
root.fingerprintsConfigured = fingerprintOutputCollector.text.includes("Fingerprints for user");
|
||||
}
|
||||
}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("fprintd-list command exited with error:", exitCode, exitStatus);
|
||||
root.fingerprintsConfigured = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PamContext {
|
||||
id: pam
|
||||
|
||||
@@ -74,6 +104,7 @@ Scope {
|
||||
onCompleted: result => {
|
||||
if (result == PamResult.Success) {
|
||||
root.unlocked(root.targetAction);
|
||||
stopFingerPam();
|
||||
} else {
|
||||
root.clearText();
|
||||
root.unlockInProgress = false;
|
||||
@@ -83,4 +114,19 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
PamContext {
|
||||
id: fingerPam
|
||||
|
||||
configDirectory: "pam"
|
||||
config: "fprintd.conf"
|
||||
|
||||
onCompleted: result => {
|
||||
if (result == PamResult.Success) {
|
||||
root.unlocked(root.targetAction);
|
||||
stopFingerPam();
|
||||
} else if (result == PamResult.Error){ // if timeout or etc..
|
||||
tryFingerUnlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,23 @@ MouseArea {
|
||||
scale: root.toolbarScale
|
||||
opacity: root.toolbarOpacity
|
||||
|
||||
// Fingerprint
|
||||
Loader {
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 6
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
active: root.context.fingerprintsConfigured
|
||||
visible: active
|
||||
|
||||
sourceComponent: MaterialSymbol {
|
||||
id: fingerprintIcon
|
||||
fill: 1
|
||||
text: "fingerprint"
|
||||
iconSize: Appearance.font.pixelSize.hugeass
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarTextField {
|
||||
id: passwordBox
|
||||
placeholderText: GlobalStates.screenUnlockFailed ? Translation.tr("Incorrect password") : Translation.tr("Enter password")
|
||||
@@ -126,7 +143,7 @@ MouseArea {
|
||||
Keys.onPressed: event => {
|
||||
root.context.resetClearTimer();
|
||||
}
|
||||
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
@@ -136,15 +153,35 @@ MouseArea {
|
||||
}
|
||||
}
|
||||
|
||||
// Shake when wrong password
|
||||
SequentialAnimation {
|
||||
id: wrongPasswordShakeAnim
|
||||
NumberAnimation { target: passwordBox; property: "x"; to: -30; duration: 50 }
|
||||
NumberAnimation { target: passwordBox; property: "x"; to: 30; duration: 50 }
|
||||
NumberAnimation { target: passwordBox; property: "x"; to: -15; duration: 40 }
|
||||
NumberAnimation { target: passwordBox; property: "x"; to: 15; duration: 40 }
|
||||
NumberAnimation { target: passwordBox; property: "x"; to: 0; duration: 30 }
|
||||
}
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onScreenUnlockFailedChanged() {
|
||||
if (GlobalStates.screenUnlockFailed) wrongPasswordShakeAnim.restart();
|
||||
}
|
||||
}
|
||||
|
||||
// We're drawing dots manually
|
||||
color: ColorUtils.transparentize(Appearance.colors.colOnLayer1)
|
||||
PasswordChars {
|
||||
property bool materialShapeChars: Config.options.lock.materialShapeChars
|
||||
color: ColorUtils.transparentize(Appearance.colors.colOnLayer1, materialShapeChars ? 1 : 0)
|
||||
Loader {
|
||||
active: passwordBox.materialShapeChars
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: passwordBox.padding
|
||||
rightMargin: passwordBox.padding
|
||||
}
|
||||
length: root.context.currentText.length
|
||||
sourceComponent: PasswordChars {
|
||||
length: root.context.currentText.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ StyledFlickable {
|
||||
Behavior on contentX {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
rightMargin: 14
|
||||
Row {
|
||||
id: dotsRow
|
||||
anchors {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
auth sufficient pam_fprintd.so
|
||||
@@ -21,7 +21,7 @@ Scope {
|
||||
readonly property real osdWidth: Appearance.sizes.osdWidth
|
||||
readonly property real widgetWidth: Appearance.sizes.mediaControlsWidth
|
||||
readonly property real widgetHeight: Appearance.sizes.mediaControlsHeight
|
||||
property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
||||
property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
|
||||
property list<real> visualizerPoints: []
|
||||
|
||||
property bool hasPlasmaIntegration: false
|
||||
|
||||
@@ -23,13 +23,25 @@ Toolbar {
|
||||
// Signals
|
||||
signal dismiss()
|
||||
|
||||
MaterialCookie {
|
||||
MaterialShape {
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 2
|
||||
Layout.rightMargin: 2
|
||||
implicitSize: 36 // Intentionally smaller because this one is brighter than others
|
||||
sides: 10
|
||||
amplitude: implicitSize / 44
|
||||
shape: switch (root.action) {
|
||||
case RegionSelection.SnipAction.Copy:
|
||||
case RegionSelection.SnipAction.Edit:
|
||||
return MaterialShape.Shape.Cookie4Sided;
|
||||
case RegionSelection.SnipAction.Search:
|
||||
return MaterialShape.Shape.Pentagon;
|
||||
case RegionSelection.SnipAction.CharRecognition:
|
||||
return MaterialShape.Shape.Sunny;
|
||||
case RegionSelection.SnipAction.Record:
|
||||
case RegionSelection.SnipAction.RecordWithSound:
|
||||
return MaterialShape.Shape.Gem;
|
||||
default:
|
||||
return MaterialShape.Shape.Cookie12Sided;
|
||||
}
|
||||
color: Appearance.colors.colPrimary
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -5,11 +5,11 @@ import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.synchronizer
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Qt.labs.synchronizer
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
|
||||
@@ -55,6 +55,20 @@ ContentPage {
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
visible: Config.options.background.clock.style === "digital"
|
||||
title: Translation.tr("Digital clock settings")
|
||||
|
||||
ConfigSwitch {
|
||||
buttonIcon: "animation"
|
||||
text: Translation.tr("Animate time change")
|
||||
checked: Config.options.background.clock.digital.animateChange
|
||||
onCheckedChanged: {
|
||||
Config.options.background.clock.digital.animateChange = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
visible: Config.options.background.clock.style === "cookie"
|
||||
title: Translation.tr("Cookie clock settings")
|
||||
@@ -71,6 +85,18 @@ ContentPage {
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSwitch {
|
||||
buttonIcon: "airwave"
|
||||
text: Translation.tr("Use old sine wave cookie implementation")
|
||||
checked: Config.options.background.clock.cookie.useSineCookie
|
||||
onCheckedChanged: {
|
||||
Config.options.background.clock.cookie.useSineCookie = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
text: "Looks a bit softer and more consistent with different number of sides,\nbut has less impressive morphing"
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSpinBox {
|
||||
icon: "add_triangle"
|
||||
text: Translation.tr("Sides")
|
||||
@@ -530,6 +556,15 @@ ContentPage {
|
||||
Config.options.lock.showLockedText = checked;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSwitch {
|
||||
buttonIcon: "shapes"
|
||||
text: Translation.tr('Use varying shapes for password characters')
|
||||
checked: Config.options.lock.materialShapeChars
|
||||
onCheckedChanged: {
|
||||
Config.options.lock.materialShapeChars = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Style: Blurred")
|
||||
|
||||
@@ -119,16 +119,17 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
property real pageKeyScrollAmount: booruResponseListView.height / 2
|
||||
Keys.onPressed: (event) => {
|
||||
tagInputField.forceActiveFocus()
|
||||
if (event.modifiers === Qt.NoModifier) {
|
||||
if (event.key === Qt.Key_PageUp) {
|
||||
if (booruResponseListView.atYBeginning) return;
|
||||
booruResponseListView.contentY = Math.max(0, booruResponseListView.contentY - booruResponseListView.height / 2)
|
||||
booruResponseListView.contentY = Math.max(0, booruResponseListView.contentY - root.pageKeyScrollAmount)
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_PageDown) {
|
||||
if (booruResponseListView.atYEnd) return;
|
||||
booruResponseListView.contentY = Math.min(booruResponseListView.contentHeight - booruResponseListView.height / 2, booruResponseListView.contentY + booruResponseListView.height / 2)
|
||||
booruResponseListView.contentY = Math.min(booruResponseListView.contentHeight, booruResponseListView.contentY + root.pageKeyScrollAmount)
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
@@ -171,17 +172,20 @@ Item {
|
||||
mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4
|
||||
|
||||
property int lastResponseLength: 0
|
||||
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
if(root.responses.length > booruResponseListView.lastResponseLength) {
|
||||
Connections {
|
||||
target: root
|
||||
function onResponsesChanged() {
|
||||
if (root.responses.length > booruResponseListView.lastResponseLength) {
|
||||
if (booruResponseListView.lastResponseLength > 0 && root.responses[booruResponseListView.lastResponseLength].provider != "system")
|
||||
booruResponseListView.contentY = booruResponseListView.contentY + root.scrollOnNewResponse
|
||||
booruResponseListView.lastResponseLength = root.responses.length
|
||||
}
|
||||
return root.responses
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: root.responses
|
||||
}
|
||||
delegate: BooruResponse {
|
||||
responseData: modelData
|
||||
tagInputField: root.inputField
|
||||
|
||||
@@ -5,6 +5,7 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Qt.labs.synchronizer
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -58,9 +59,8 @@ Item {
|
||||
id: tabBar
|
||||
visible: root.tabButtonList.length > 1
|
||||
tabButtonList: root.tabButtonList
|
||||
externalTrackedTab: root.selectedTab
|
||||
function onCurrentIndexChanged(currentIndex) {
|
||||
root.selectedTab = currentIndex
|
||||
Synchronizer on currentIndex {
|
||||
property alias source: root.selectedTab
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ Item {
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
|
||||
currentIndex: tabBar.externalTrackedTab
|
||||
currentIndex: root.selectedTab
|
||||
onCurrentIndexChanged: {
|
||||
tabBar.enableIndicatorAnimation = true
|
||||
root.selectedTab = currentIndex
|
||||
|
||||
@@ -105,7 +105,7 @@ Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 7
|
||||
spacing: 12
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
@@ -177,6 +177,20 @@ Rectangle {
|
||||
ButtonGroup {
|
||||
spacing: 5
|
||||
|
||||
AiMessageControlButton {
|
||||
id: regenButton
|
||||
buttonIcon: "refresh"
|
||||
visible: messageData?.role === 'assistant'
|
||||
|
||||
onClicked: {
|
||||
Ai.regenerate(root.messageIndex)
|
||||
}
|
||||
|
||||
StyledToolTip {
|
||||
text: Translation.tr("Regenerate")
|
||||
}
|
||||
}
|
||||
|
||||
AiMessageControlButton {
|
||||
id: copyButton
|
||||
buttonIcon: activated ? "inventory" : "content_copy"
|
||||
@@ -254,28 +268,50 @@ Rectangle {
|
||||
|
||||
spacing: 0
|
||||
Repeater {
|
||||
model: root.messageBlocks.length
|
||||
delegate: Loader {
|
||||
required property int index
|
||||
property var thisBlock: root.messageBlocks[index]
|
||||
Layout.fillWidth: true
|
||||
// property var segment: thisBlock
|
||||
property var segmentContent: thisBlock.content
|
||||
property var segmentLang: thisBlock.lang
|
||||
property var messageData: root.messageData
|
||||
property var editing: root.editing
|
||||
property var renderMarkdown: root.renderMarkdown
|
||||
property var enableMouseSelection: root.enableMouseSelection
|
||||
property bool thinking: root.messageData?.thinking ?? true
|
||||
property bool done: root.messageData?.done ?? false
|
||||
property bool completed: thisBlock.completed ?? false
|
||||
model: ScriptModel {
|
||||
values: Array.from({ length: root.messageBlocks.length }, (msg, i) => {
|
||||
return ({
|
||||
type: root.messageBlocks[i].type
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
property bool forceDisableChunkSplitting: root.messageData.content.includes("```")
|
||||
|
||||
source: thisBlock.type === "code" ? "MessageCodeBlock.qml" :
|
||||
thisBlock.type === "think" ? "MessageThinkBlock.qml" :
|
||||
"MessageTextBlock.qml"
|
||||
delegate: DelegateChooser {
|
||||
id: messageDelegate
|
||||
role: "type"
|
||||
|
||||
DelegateChoice { roleValue: "code"; MessageCodeBlock {
|
||||
required property int index
|
||||
property var thisBlock: root.messageBlocks[index]
|
||||
editing: root.editing
|
||||
renderMarkdown: root.renderMarkdown
|
||||
enableMouseSelection: root.enableMouseSelection
|
||||
segmentContent: thisBlock.content
|
||||
segmentLang: thisBlock.lang
|
||||
messageData: root.messageData
|
||||
} }
|
||||
DelegateChoice { roleValue: "think"; MessageThinkBlock {
|
||||
required property int index
|
||||
property var thisBlock: root.messageBlocks[index]
|
||||
editing: root.editing
|
||||
renderMarkdown: root.renderMarkdown
|
||||
enableMouseSelection: root.enableMouseSelection
|
||||
segmentContent: thisBlock.content
|
||||
messageData: root.messageData
|
||||
done: root.messageData?.done ?? false
|
||||
completed: thisBlock.completed ?? false
|
||||
} }
|
||||
DelegateChoice { roleValue: "text"; MessageTextBlock {
|
||||
required property int index
|
||||
property var thisBlock: root.messageBlocks[index]
|
||||
editing: root.editing
|
||||
renderMarkdown: root.renderMarkdown
|
||||
enableMouseSelection: root.enableMouseSelection
|
||||
segmentContent: thisBlock.content
|
||||
messageData: root.messageData
|
||||
done: root.messageData?.done ?? false
|
||||
forceDisableChunkSplitting: root.messageData.content.includes("```")
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,22 +13,20 @@ import org.kde.syntaxhighlighting
|
||||
ColumnLayout {
|
||||
id: root
|
||||
// These are needed on the parent loader
|
||||
property bool editing: parent?.editing ?? false
|
||||
property bool renderMarkdown: parent?.renderMarkdown ?? true
|
||||
property bool enableMouseSelection: parent?.enableMouseSelection ?? false
|
||||
property var segmentContent: parent?.segmentContent ?? ({})
|
||||
property var segmentLang: parent?.segmentLang ?? "txt"
|
||||
property bool editing: false
|
||||
property bool renderMarkdown: true
|
||||
property bool enableMouseSelection: false
|
||||
property var segmentContent: ({})
|
||||
property var segmentLang: "txt"
|
||||
property var messageData: {}
|
||||
property bool isCommandRequest: segmentLang === "command"
|
||||
property var displayLang: (isCommandRequest ? "bash" : segmentLang)
|
||||
property var messageData: parent?.messageData ?? {}
|
||||
|
||||
property real codeBlockBackgroundRounding: Appearance.rounding.small
|
||||
property real codeBlockHeaderPadding: 3
|
||||
property real codeBlockComponentSpacing: 2
|
||||
|
||||
spacing: codeBlockComponentSpacing
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Rectangle { // Code background
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -14,17 +14,17 @@ import Quickshell.Hyprland
|
||||
ColumnLayout {
|
||||
id: root
|
||||
// These are needed on the parent loader
|
||||
property bool editing: parent?.editing ?? false
|
||||
property bool renderMarkdown: parent?.renderMarkdown ?? true
|
||||
property bool enableMouseSelection: parent?.enableMouseSelection ?? false
|
||||
property string segmentContent: parent?.segmentContent ?? ({})
|
||||
property var messageData: parent?.messageData ?? {}
|
||||
property bool done: parent?.done ?? true
|
||||
property list<string> renderedLatexHashes: []
|
||||
property bool editing: false
|
||||
property bool renderMarkdown: true
|
||||
property bool enableMouseSelection: false
|
||||
property var segmentContent: ({})
|
||||
property var messageData: {}
|
||||
property bool done: true
|
||||
property bool forceDisableChunkSplitting: false
|
||||
|
||||
property list<string> renderedLatexHashes: []
|
||||
property string renderedSegmentContent: ""
|
||||
property string shownText: ""
|
||||
property bool forceDisableChunkSplitting: parent?.forceDisableChunkSplitting ?? false
|
||||
property bool fadeChunkSplitting: !forceDisableChunkSplitting && !editing && !/\n\|/.test(shownText) && Config.options.sidebar.ai.textFadeIn
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -11,13 +11,13 @@ import Qt5Compat.GraphicalEffects
|
||||
Item {
|
||||
id: root
|
||||
// These are needed on the parent loader
|
||||
property bool editing: parent?.editing ?? false
|
||||
property bool renderMarkdown: parent?.renderMarkdown ?? true
|
||||
property bool enableMouseSelection: parent?.enableMouseSelection ?? false
|
||||
property string segmentContent: parent?.segmentContent ?? ({})
|
||||
property var messageData: parent?.messageData ?? {}
|
||||
property bool done: parent?.done ?? true
|
||||
property bool completed: parent?.completed ?? false
|
||||
property bool editing: false
|
||||
property bool renderMarkdown: true
|
||||
property bool enableMouseSelection: false
|
||||
property var segmentContent: ({})
|
||||
property var messageData: {}
|
||||
property bool done: true
|
||||
property bool completed: false
|
||||
|
||||
property real thinkBlockBackgroundRounding: Appearance.rounding.small
|
||||
property real thinkBlockHeaderPaddingVertical: 3
|
||||
|
||||
+4
-4
@@ -17,10 +17,10 @@ AndroidQuickToggleButton {
|
||||
case PowerProfile.Performance: return "local_fire_department"
|
||||
}
|
||||
statusText: switch(PowerProfiles.profile) {
|
||||
case PowerProfile.PowerSaver: return "Power Saver"
|
||||
case PowerProfile.Balanced: return "Balanced"
|
||||
case PowerProfile.Performance: return "Performance"
|
||||
}
|
||||
case PowerProfile.PowerSaver: return "Power Saver"
|
||||
case PowerProfile.Balanced: return "Balanced"
|
||||
case PowerProfile.Performance: return "Performance"
|
||||
}
|
||||
|
||||
onClicked: (event) => {
|
||||
if (PowerProfiles.hasPerformanceProfile) {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
COLOR_FILE_PATH="${XDG_STATE_HOME:-$HOME/.local/state}/quickshell/user/generated/color.txt"
|
||||
|
||||
# Define an array of possible VSCode settings file paths for various forks
|
||||
settings_paths=(
|
||||
"${XDG_CONFIG_HOME:-$HOME/.config}/Code/User/settings.json"
|
||||
"${XDG_CONFIG_HOME:-$HOME/.config}/VSCodium/User/settings.json"
|
||||
"${XDG_CONFIG_HOME:-$HOME/.config}/Code - OSS/User/settings.json"
|
||||
"${XDG_CONFIG_HOME:-$HOME/.config}/Code - Insiders/User/settings.json"
|
||||
"${XDG_CONFIG_HOME:-$HOME/.config}/Cursor/User/settings.json"
|
||||
# Add more paths as needed for other forks
|
||||
)
|
||||
|
||||
new_color=$(cat "$COLOR_FILE_PATH")
|
||||
|
||||
# Loop through each settings file path
|
||||
for CODE_SETTINGS_PATH in "${settings_paths[@]}"; do
|
||||
if [[ -f "$CODE_SETTINGS_PATH" ]]; then
|
||||
# Try to update the key if it exists
|
||||
if grep -q '"material-code.primaryColor"' "$CODE_SETTINGS_PATH"; then
|
||||
sed -i -E \
|
||||
"s/(\"material-code.primaryColor\"\s*:\s*\")[^\"]*(\")/\1${new_color}\2/" \
|
||||
"$CODE_SETTINGS_PATH"
|
||||
else # If the key is not already there, add it
|
||||
sed -i '$ s/}/,\n "material-code.primaryColor": "'${new_color}'"\n}/' "$CODE_SETTINGS_PATH"
|
||||
sed -i '$ s/,\n,/,/' "$CODE_SETTINGS_PATH"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -55,18 +55,8 @@ post_process() {
|
||||
local screen_height="$2"
|
||||
local wallpaper_path="$3"
|
||||
|
||||
|
||||
handle_kde_material_you_colors &
|
||||
|
||||
# Determine the largest region on the wallpaper that's sufficiently un-busy to put widgets in
|
||||
# if [ ! -f "$MATUGEN_DIR/scripts/least_busy_region.py" ]; then
|
||||
# echo "Error: least_busy_region.py script not found in $MATUGEN_DIR/scripts/"
|
||||
# else
|
||||
# "$MATUGEN_DIR/scripts/least_busy_region.py" \
|
||||
# --screen-width "$screen_width" --screen-height "$screen_height" \
|
||||
# --width 300 --height 200 \
|
||||
# "$wallpaper_path" > "$STATE_DIR"/user/generated/wallpaper/least_busy_region.json
|
||||
# fi
|
||||
"$SCRIPT_DIR/code/material-code-set-color.sh" &
|
||||
}
|
||||
|
||||
check_and_prompt_upscale() {
|
||||
|
||||
@@ -769,6 +769,18 @@ Singleton {
|
||||
root.pendingFilePath = CF.FileUtils.trimFileProtocol(filePath);
|
||||
}
|
||||
|
||||
function regenerate(messageIndex) {
|
||||
if (messageIndex < 0 || messageIndex >= messageIDs.length) return;
|
||||
const id = root.messageIDs[messageIndex];
|
||||
const message = root.messageByID[id];
|
||||
if (message.role !== "assistant") return;
|
||||
// Remove all messages after this one
|
||||
for (let i = root.messageIDs.length - 1; i >= messageIndex; i--) {
|
||||
root.removeMessage(i);
|
||||
}
|
||||
requester.makeRequest();
|
||||
}
|
||||
|
||||
function createFunctionOutputMessage(name, output, includeOutputInChat = true) {
|
||||
return aiMessageComponent.createObject(root, {
|
||||
"role": "user",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
/**
|
||||
* A nice wrapper for default Pipewire audio sink and source.
|
||||
@@ -29,12 +29,18 @@ Singleton {
|
||||
property real lastVolume: 0
|
||||
function onVolumeChanged() {
|
||||
if (!Config.options.audio.protection.enable) return;
|
||||
const newVolume = sink.audio.volume;
|
||||
// when resuming from suspend, we should not write volume to avoid pipewire volume reset issues
|
||||
if (isNaN(newVolume) || newVolume === undefined || newVolume === null) {
|
||||
lastReady = false;
|
||||
lastVolume = 0;
|
||||
return;
|
||||
}
|
||||
if (!lastReady) {
|
||||
lastVolume = sink.audio.volume;
|
||||
lastVolume = newVolume;
|
||||
lastReady = true;
|
||||
return;
|
||||
}
|
||||
const newVolume = sink.audio.volume;
|
||||
const maxAllowedIncrease = Config.options.audio.protection.maxAllowedIncrease / 100;
|
||||
const maxAllowed = Config.options.audio.protection.maxAllowed / 100;
|
||||
|
||||
@@ -45,9 +51,6 @@ Singleton {
|
||||
root.sinkProtectionTriggered(Translation.tr("Exceeded max allowed"));
|
||||
sink.audio.volume = Math.min(lastVolume, maxAllowed);
|
||||
}
|
||||
if (sink.ready && (isNaN(sink.audio.volume) || sink.audio.volume === undefined || sink.audio.volume === null)) {
|
||||
sink.audio.volume = 0;
|
||||
}
|
||||
lastVolume = sink.audio.volume;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pkgname=illogical-impulse-audio
|
||||
pkgver=1.0
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc='Illogical Impulse Audio Dependencies'
|
||||
arch=(any)
|
||||
license=(None)
|
||||
@@ -8,6 +8,7 @@ depends=(
|
||||
cava
|
||||
pavucontrol-qt
|
||||
wireplumber
|
||||
pipewire-pulse
|
||||
libdbusmenu-gtk3
|
||||
playerctl
|
||||
)
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
pkgname=illogical-impulse-basic
|
||||
pkgver=1.0
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc='Illogical Impulse Basic Dependencies'
|
||||
arch=(any)
|
||||
license=(None)
|
||||
depends=(
|
||||
axel
|
||||
bc
|
||||
coreutils
|
||||
cliphist
|
||||
cmake
|
||||
curl
|
||||
rsync
|
||||
wget
|
||||
ripgrep
|
||||
jq
|
||||
meson
|
||||
xdg-user-dirs
|
||||
axel
|
||||
bc
|
||||
coreutils
|
||||
cliphist
|
||||
cmake
|
||||
curl
|
||||
wget
|
||||
ripgrep
|
||||
jq
|
||||
meson
|
||||
xdg-user-dirs
|
||||
# Used in install script
|
||||
rsync
|
||||
go-yq # https://github.com/mikefarah/yq
|
||||
)
|
||||
|
||||
+1
@@ -25,4 +25,5 @@ RDEPEND="
|
||||
dev-python/jq
|
||||
dev-build/meson
|
||||
x11-misc/xdg-user-dirs
|
||||
app-misc/yq-go
|
||||
"
|
||||
@@ -6,24 +6,10 @@
|
||||
**NOTE: The sdata/dist-nix is not for NixOS but every distro, using Nix and home-manager.**
|
||||
|
||||
## plan
|
||||
TODO:
|
||||
Write a proper `flake.nix` and optionally `home.nix` and other files under `./sdata/dist-nix/iiqs-hm/` to install all dependencies that `./sdata/dist-arch/install-deps.sh` does. (**excluding** the screenlock)
|
||||
|
||||
TODO:
|
||||
In this script, implement the process below:
|
||||
1. Warning user about "this script is only experimental and must only use it at your own risks.", and prompt `y/N` (default N) before proceeding.
|
||||
2. If nix not installed:
|
||||
1. install nix via [NixOS/experimental-nix-installer](https://github.com/NixOS/experimental-nix-installer)
|
||||
2. Enable nix for shell
|
||||
- Update: Skip this step cuz the nix-installer will handle it automatically e.g. in `/etc/zsh/zshrc`.
|
||||
3. Ensure the experimental feature, Nix Flake, is enabled.
|
||||
3. cd to `iiqs-hm` and use something like `home-manager switch --flake .#iiqs` to install the dependencies.
|
||||
4. Install screen lock using system package manager of the current distro.
|
||||
|
||||
Note that this script must be idempotent.
|
||||
|
||||
TODO:
|
||||
Write guide for people already use nix, so they can manually grab things from this repo to their own Nix/home-manager configurations to install the dependencies.
|
||||
- [ ] 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)
|
||||
|
||||
## Attentions
|
||||
### PAM
|
||||
@@ -37,61 +23,4 @@ The problem could be solved by using the system-provided libpam instead.
|
||||
See also https://github.com/caelestia-dots/shell/issues/668
|
||||
|
||||
### NixGL
|
||||
On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. `nixGL` should be used to address the problem. Example code in `home.nix`:
|
||||
```
|
||||
{ config, lib, pkgs, nixgl, ... }:
|
||||
{
|
||||
nixGL.packages = nixgl.packages;
|
||||
nixGL.defaultWrapper = "mesa";
|
||||
|
||||
# other lines not showed here ...
|
||||
|
||||
home = {
|
||||
packages = with pkgs; [
|
||||
cowsay # normal packages that does not need nixGL
|
||||
lolcat
|
||||
# other lines not showed here ...
|
||||
]
|
||||
++ [
|
||||
(config.lib.nixGL.wrap pkgs.firefox-bin)
|
||||
(config.lib.nixGL.wrap pkgs.hyprland)
|
||||
# other lines not showed here ...
|
||||
];
|
||||
# other lines not showed here ...
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
And in `flake.nix`:
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager/release-25.05";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
hyprland = {
|
||||
url = "github:hyprwm/Hyprland";
|
||||
};
|
||||
nixgl.url = "github:nix-community/nixGL";
|
||||
};
|
||||
outputs = { nixpkgs, home-manager, nixgl, ... }:
|
||||
let
|
||||
lib = nixpkgs.lib;
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ nixgl.overlay ];
|
||||
};
|
||||
in {
|
||||
homeConfigurations = {
|
||||
mydot = home-manager.lib.homeManagerConfiguration {
|
||||
inherit pkgs;
|
||||
extraSpecialArgs = { inherit nixgl; };
|
||||
modules = [ ./home.nix ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. `nixGL` should be used to address the problem.
|
||||
|
||||
Generated
+542
@@ -0,0 +1,542 @@
|
||||
{
|
||||
"nodes": {
|
||||
"aquamarine": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"hyprland",
|
||||
"hyprutils"
|
||||
],
|
||||
"hyprwayland-scanner": [
|
||||
"hyprland",
|
||||
"hyprwayland-scanner"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760101617,
|
||||
"narHash": "sha256-8jf/3ZCi+B7zYpIyV04+3wm72BD7Z801IlOzsOACR7I=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "aquamarine",
|
||||
"rev": "1826a9923881320306231b1c2090379ebf9fa4f8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "aquamarine",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758463745,
|
||||
"narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "release-25.05",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprcursor": {
|
||||
"inputs": {
|
||||
"hyprlang": [
|
||||
"hyprland",
|
||||
"hyprlang"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1753964049,
|
||||
"narHash": "sha256-lIqabfBY7z/OANxHoPeIrDJrFyYy9jAM4GQLzZ2feCM=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprcursor",
|
||||
"rev": "44e91d467bdad8dcf8bbd2ac7cf49972540980a5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprcursor",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprgraphics": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"hyprland",
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760445448,
|
||||
"narHash": "sha256-fXGjL6dw31FPFRrmIemzGiNSlfvEJTJNsmadZi+qNhI=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"rev": "50fb9f069219f338a11cf0bcccb9e58357d67757",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland": {
|
||||
"inputs": {
|
||||
"aquamarine": "aquamarine",
|
||||
"hyprcursor": "hyprcursor",
|
||||
"hyprgraphics": "hyprgraphics",
|
||||
"hyprland-protocols": "hyprland-protocols",
|
||||
"hyprland-qtutils": "hyprland-qtutils",
|
||||
"hyprlang": "hyprlang",
|
||||
"hyprutils": "hyprutils",
|
||||
"hyprwayland-scanner": "hyprwayland-scanner",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks",
|
||||
"systems": "systems",
|
||||
"xdph": "xdph"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1761780088,
|
||||
"narHash": "sha256-ylKrWQeIAGyysfHbgZpcWUs9UsbiOBIVXTPqaiV3lf0=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "Hyprland",
|
||||
"rev": "6ade4d58cab67e18aa758ef664e36421cab4d8b2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "Hyprland",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-protocols": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759610243,
|
||||
"narHash": "sha256-+KEVnKBe8wz+a6dTLq8YDcF3UrhQElwsYJaVaHXJtoI=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-protocols",
|
||||
"rev": "bd153e76f751f150a09328dbdeb5e4fab9d23622",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-protocols",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-qt-support": {
|
||||
"inputs": {
|
||||
"hyprlang": [
|
||||
"hyprland",
|
||||
"hyprland-qtutils",
|
||||
"hyprlang"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"hyprland-qtutils",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"hyprland-qtutils",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749154592,
|
||||
"narHash": "sha256-DO7z5CeT/ddSGDEnK9mAXm1qlGL47L3VAHLlLXoCjhE=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"rev": "4c8053c3c888138a30c3a6c45c2e45f5484f2074",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-qtutils": {
|
||||
"inputs": {
|
||||
"hyprland-qt-support": "hyprland-qt-support",
|
||||
"hyprlang": [
|
||||
"hyprland",
|
||||
"hyprlang"
|
||||
],
|
||||
"hyprutils": [
|
||||
"hyprland",
|
||||
"hyprland-qtutils",
|
||||
"hyprlang",
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759080228,
|
||||
"narHash": "sha256-RgDoAja0T1hnF0pTc56xPfLfFOO8Utol2iITwYbUhTk=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qtutils",
|
||||
"rev": "629b15c19fa4082e4ce6be09fdb89e8c3312aed7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qtutils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprlang": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"hyprland",
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758927902,
|
||||
"narHash": "sha256-LZgMds7M94+vuMql2bERQ6LiFFdhgsEFezE4Vn+Ys3A=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"rev": "4dafa28d4f79877d67a7d1a654cddccf8ebf15da",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprutils": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759619523,
|
||||
"narHash": "sha256-r1ed7AR2ZEb2U8gy321/Xcp1ho2tzn+gG1te/Wxsj1A=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "3df7bde01efb3a3e8e678d1155f2aa3f19e177ef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprwayland-scanner": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755184602,
|
||||
"narHash": "sha256-RCBQN8xuADB0LEgaKbfRqwm6CdyopE1xIEhNc67FAbw=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"rev": "b3b0f1f40ae09d4447c20608e5a4faf8bf3c492d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixgl": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1752054764,
|
||||
"narHash": "sha256-Ob/HuUhANoDs+nvYqyTKrkcPXf4ZgXoqMTQoCK0RFgQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixGL",
|
||||
"rev": "a8e1ce7d49a149ed70df676785b07f63288f53c5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixGL",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1761114652,
|
||||
"narHash": "sha256-f/QCJM/YhrV/lavyCVz8iU3rlZun6d+dAiC3H+CDle4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "01f116e4df6a15f4ccdffb1bcd41096869fb385c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1746378225,
|
||||
"narHash": "sha256-OeRSuL8PUjIfL3Q0fTbNJD/fmv1R+K2JAOqWJd3Oceg=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "93e8cdce7afc64297cfec447c311470788131cd9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1761597516,
|
||||
"narHash": "sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8+ON/0Yy8+a5vsDU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "daf6dc47aa4b44791372d6139ab7b25269184d55",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-25.05",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760663237,
|
||||
"narHash": "sha256-BflA6U4AM1bzuRMR8QqzPXqh8sWVCNDzOdsxXEguJIc=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"home-manager": "home-manager",
|
||||
"hyprland": "hyprland",
|
||||
"nixgl": "nixgl",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1689347949,
|
||||
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"xdph": {
|
||||
"inputs": {
|
||||
"hyprland-protocols": [
|
||||
"hyprland",
|
||||
"hyprland-protocols"
|
||||
],
|
||||
"hyprlang": [
|
||||
"hyprland",
|
||||
"hyprlang"
|
||||
],
|
||||
"hyprutils": [
|
||||
"hyprland",
|
||||
"hyprutils"
|
||||
],
|
||||
"hyprwayland-scanner": [
|
||||
"hyprland",
|
||||
"hyprwayland-scanner"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760713634,
|
||||
"narHash": "sha256-5HXelmz2x/uO26lvW7MudnadbAfoBnve4tRBiDVLtOM=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "xdg-desktop-portal-hyprland",
|
||||
"rev": "753bbbdf6a052994da94062e5b753288cef28dfb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "xdg-desktop-portal-hyprland",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
# flake.nix
|
||||
{
|
||||
description = "illogical-impulse";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager/release-25.05";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
hyprland = {
|
||||
url = "github:hyprwm/Hyprland";
|
||||
};
|
||||
nixgl.url = "github:nix-community/nixGL";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, home-manager, nixgl, ... }:
|
||||
let
|
||||
home_attrs = rec {
|
||||
username = import ./username.nix;
|
||||
homeDirectory = "/home/${username}";
|
||||
stateVersion = "25.05";
|
||||
};
|
||||
system = "x86_64-linux";
|
||||
lib = nixpkgs.lib;
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in {
|
||||
homeConfigurations = {
|
||||
illogical_impulse = home-manager.lib.homeManagerConfiguration {
|
||||
inherit pkgs;
|
||||
extraSpecialArgs = { inherit nixgl home_attrs; };
|
||||
modules = [
|
||||
./home.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{ config, lib, pkgs, nixgl, home_attrs, ... }:
|
||||
{
|
||||
programs.home-manager.enable = true;
|
||||
nixGL.packages = nixgl.packages;
|
||||
nixGL.defaultWrapper = "mesa";
|
||||
|
||||
xdg.portal = {
|
||||
enable = true;
|
||||
extraPortals = with pkgs; [
|
||||
xdg-desktop-portal-gnome
|
||||
xdg-desktop-portal-gtk
|
||||
xdg-desktop-portal-wlr
|
||||
];
|
||||
config.hyprland = {
|
||||
default = [ "hyprland" "gtk" ];
|
||||
"org.freedesktop.impl.portal.ScreenCast" = [
|
||||
"gnome"
|
||||
];
|
||||
};
|
||||
};
|
||||
## Allow fontconfig to discover fonts in home.packages
|
||||
fonts.fontconfig.enable = true;
|
||||
|
||||
# home.sessionVariables.NIXOS_OZONE_WL = "1";
|
||||
wayland.windowManager.hyprland = {
|
||||
## Make sure home-manager not generate ~/.config/hypr/hyprland.conf
|
||||
systemd.enable = false; plugins = []; settings = {}; extraConfig = "";
|
||||
enable = true;
|
||||
## Use NixGL
|
||||
package = config.lib.nixGL.wrap pkgs.hyprland;
|
||||
};
|
||||
|
||||
home = {
|
||||
packages = with pkgs; [
|
||||
##### Sure #####
|
||||
## Basic cli tool
|
||||
## inetutils: provides hostname, ifconfig, ping, etc.
|
||||
## libnotify: provides notify-send
|
||||
jq rsync inetutils libnotify
|
||||
## Media related
|
||||
brightnessctl playerctl pavucontrol
|
||||
## Clipboard/Emoji
|
||||
wl-clipboard cliphist
|
||||
## Terminal and shell
|
||||
foot cowsay lolcat
|
||||
|
||||
##### Fonts/Icons/Cursors/Decoration #####
|
||||
fontconfig
|
||||
|
||||
##### Other basic things #####
|
||||
dbus xorg.xlsclients networkmanager
|
||||
|
||||
##### Not work, to be solved #####
|
||||
# swaylock pamtester
|
||||
];
|
||||
}//home_attrs;
|
||||
}
|
||||
@@ -1,4 +1,129 @@
|
||||
# This script is meant to be sourced.
|
||||
# It's not for directly running.
|
||||
|
||||
# This file is currently WIP.
|
||||
function install_home-manager(){
|
||||
# https://nix-community.github.io/home-manager/index.xhtml#sec-install-standalone
|
||||
local cmd=home-manager
|
||||
# Maybe installed already, just not sourced yet
|
||||
try source $HOME/.nix-profile/etc/profile.d/hm-session-vars.sh
|
||||
command -v $cmd && return
|
||||
|
||||
x nix-channel --add https://nixos.org/channels/nixos-25.05 nixpkgs-home
|
||||
x nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.05.tar.gz home-manager
|
||||
x nix-channel --update
|
||||
x env NIX_PATH="nixpkgs=$HOME/.nix-defexpr/channels/nixpkgs-home" nix-shell '<home-manager>' -A install
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
function install_nix(){
|
||||
# https://github.com/NixOS/experimental-nix-installer
|
||||
local cmd=nix
|
||||
|
||||
x mkdir -p ${REPO_ROOT}/cache
|
||||
x curl -JLo ${REPO_ROOT}/cache/nix-installer https://artifacts.nixos.org/experimental-installer
|
||||
x sh ${REPO_ROOT}/cache/nix-installer install
|
||||
try source '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
function install_curl(){
|
||||
local cmd=curl
|
||||
|
||||
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||
x sudo pacman -Syu
|
||||
x sudo pacman -S --noconfirm $cmd
|
||||
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||
x sudo apt update
|
||||
x sudo apt install $cmd
|
||||
fi
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
function install_zsh(){
|
||||
local cmd=zsh
|
||||
|
||||
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||
x sudo pacman -Syu
|
||||
x sudo pacman -S --noconfirm $cmd
|
||||
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||
x sudo apt update
|
||||
x sudo apt install $cmd
|
||||
fi
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
function install_swaylock(){
|
||||
local cmd=swaylock
|
||||
echo "Detecting command \"$cmd\"..."
|
||||
command -v $cmd && return
|
||||
echo "Command \"$cmd\" not found, try to install..."
|
||||
|
||||
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||
x sudo pacman -Syu
|
||||
x sudo pacman -S --noconfirm $cmd
|
||||
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||
x sudo apt update
|
||||
x sudo apt install $cmd
|
||||
fi
|
||||
|
||||
command -v $cmd && return
|
||||
echo "Failed in installing $cmd."
|
||||
echo "Please install it by yourself and then retry."
|
||||
return 1
|
||||
}
|
||||
|
||||
function hm_deps(){
|
||||
SETUP_HM_DIR="${REPO_ROOT}/sdata/dist-nix/home-manager"
|
||||
SETUP_USERNAME_NIXFILE="${SETUP_HM_DIR}/username.nix"
|
||||
echo "\"$(whoami)\"" > "${SETUP_USERNAME_NIXFILE}"
|
||||
x git add "${SETUP_USERNAME_NIXFILE}"
|
||||
cd $SETUP_HM_DIR
|
||||
x home-manager switch --flake .#illogical_impulse \
|
||||
--extra-experimental-features nix-command \
|
||||
--extra-experimental-features flakes
|
||||
cd $REPO_ROOT
|
||||
x git rm -f "${SETUP_USERNAME_NIXFILE}"
|
||||
}
|
||||
|
||||
##################################################
|
||||
##################################################
|
||||
if ! command -v curl >/dev/null 2>&1;then
|
||||
echo -e "${STY_YELLOW}[$0]: \"curl\" not found.${STY_RST}"
|
||||
showfun install_curl
|
||||
v install_curl
|
||||
fi
|
||||
if ! command -v zsh >/dev/null 2>&1;then
|
||||
echo -e "${STY_YELLOW}[$0]: \"zsh\" not found.${STY_RST}"
|
||||
showfun install_zsh
|
||||
v install_zsh
|
||||
fi
|
||||
if ! command -v swaylock >/dev/null 2>&1;then
|
||||
echo -e "${STY_YELLOW}[$0]: \"swaylock\" not found.${STY_RST}"
|
||||
showfun install_swaylock
|
||||
v install_swaylock
|
||||
fi
|
||||
if ! command -v nix >/dev/null 2>&1;then
|
||||
echo -e "${STY_YELLOW}[$0]: \"nix\" not found.${STY_RST}"
|
||||
showfun install_nix
|
||||
v install_nix
|
||||
fi
|
||||
if ! command -v home-manager >/dev/null 2>&1;then
|
||||
echo -e "${STY_YELLOW}[$0]: \"home-manager\" not found.${STY_RST}"
|
||||
showfun install_home-manager
|
||||
v install_home-manager
|
||||
fi
|
||||
|
||||
showfun hm_deps
|
||||
v hm_deps
|
||||
|
||||
+51
-1
@@ -69,7 +69,11 @@ function pause(){
|
||||
fi
|
||||
}
|
||||
function remove_bashcomments_emptylines(){
|
||||
mkdir -p "$(dirname "$2")" && cat "$1" | sed -e 's/#.*//' -e '/^[[:space:]]*$/d' > "$2"
|
||||
echo "pwd=$(pwd)"
|
||||
echo "input=$1"
|
||||
echo "output=$2"
|
||||
mkdir -p "$(dirname "$2")"
|
||||
cat "$1" | sed -e 's/#.*//' -e '/^[[:space:]]*$/d' > "$2"
|
||||
}
|
||||
function prevent_sudo_or_root(){
|
||||
case $(whoami) in
|
||||
@@ -309,3 +313,49 @@ function auto_get_git_submodule(){
|
||||
x git submodule update --init --recursive
|
||||
fi
|
||||
}
|
||||
|
||||
function backup_clashing_targets(){
|
||||
# For non-recursive dirs/files under target_dir, only backup those which clashes with the ones under source_dir
|
||||
# However, ignore the ones listed in ignored_list
|
||||
|
||||
# Deal with arguments
|
||||
local source_dir="$1"
|
||||
local target_dir="$2"
|
||||
local backup_dir="$3"
|
||||
local -a ignored_list=("${@:4}")
|
||||
|
||||
# Find clash dirs/files, save as clash_list
|
||||
local clash_list=()
|
||||
local source_list=($(ls -A "$source_dir"))
|
||||
local target_list=($(ls -A "$target_dir"))
|
||||
local -A target_map
|
||||
for i in "${target_list[@]}"; do
|
||||
target_map["$i"]=1
|
||||
done
|
||||
for i in "${source_list[@]}"; do
|
||||
if [[ -n "${target_map[$i]}" ]]; then
|
||||
clash_list+=("$i")
|
||||
fi
|
||||
done
|
||||
local -A delk
|
||||
for del in "${ignored_list[@]}" ; do delk[$del]=1 ; done
|
||||
for k in "${!clash_list[@]}" ; do
|
||||
[ "${delk[${clash_list[$k]}]-}" ] && unset 'clash_list[k]'
|
||||
done
|
||||
clash_list=("${clash_list[@]}")
|
||||
|
||||
# Construct args_includes for rsync
|
||||
local args_includes=()
|
||||
for i in "${clash_list[@]}"; do
|
||||
if [[ -d "$target_dir/$i" ]]; then
|
||||
args_includes+=(--include="/$i/")
|
||||
args_includes+=(--include="/$i/**")
|
||||
else
|
||||
args_includes+=(--include="/$i")
|
||||
fi
|
||||
done
|
||||
args_includes+=(--exclude='*')
|
||||
|
||||
x mkdir -p $backup_dir
|
||||
x rsync -av --progress "${args_includes[@]}" "$target_dir/" "$backup_dir/"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# Check whether pkgs exist in AUR or repos of Arch.
|
||||
#
|
||||
# Do NOT abuse this since it consumes extra bandwidth from AUR server.
|
||||
|
||||
pkglistfile=$(mktemp)
|
||||
pkglistfile_orig=${LIST_FILE_PATH}
|
||||
pkglistfile_orig_s=${REPO_ROOT}/cache/dependencies_stripped.conf
|
||||
#if ! "$(command -v curl)";then
|
||||
# echo "Please install curl first.";exit 1
|
||||
#fi
|
||||
#if ! "$(command -v gzip)";then
|
||||
# echo "Please install gzip first.";exit 1
|
||||
#fi
|
||||
#if ! "$(command -v pacman)";then
|
||||
# echo "pacman not found, aborting...";exit 1
|
||||
#fi
|
||||
remove_bashcomments_emptylines $pkglistfile_orig $pkglistfile_orig_s
|
||||
|
||||
cat $pkglistfile_orig_s | sed "s_\ _\n_g" > $pkglistfile
|
||||
|
||||
echo "The non-existent pkgs in $pkglistfile_orig are listed as follows."
|
||||
# Borrowed from https://bbs.archlinux.org/viewtopic.php?pid=1490795#p1490795
|
||||
comm -23 <(sort -u $pkglistfile) <(sort -u <(curl https://aur.archlinux.org/packages.gz | gzip -cd | sort) <(pacman -Ssq))
|
||||
echo "End of list. If nothing appears, then all pkgs exist."
|
||||
rm $pkglistfile
|
||||
@@ -0,0 +1,33 @@
|
||||
# Handle args for subcmd: checkdeps
|
||||
# shellcheck shell=bash
|
||||
|
||||
showhelp(){
|
||||
echo -e "Syntax: $0 checkdeps [OPTIONS] <LIST_FILE_PATH>...
|
||||
|
||||
Check whether pkgs listed in <LIST_FILE_PATH> exist in AUR or repos of Arch.
|
||||
|
||||
Options:
|
||||
-h, --help Show this help message
|
||||
"
|
||||
}
|
||||
# `man getopt` to see more
|
||||
para=$(getopt \
|
||||
-o h \
|
||||
-l help \
|
||||
-n "$0" -- "$@")
|
||||
[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1
|
||||
#####################################################################################
|
||||
eval set -- "$para"
|
||||
while true ; do
|
||||
case "$1" in
|
||||
-h|--help) showhelp;exit;;
|
||||
--) shift;break ;;
|
||||
*) sleep 0 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -f "$1" ]]; then
|
||||
echo "Using list file \"$1\".";LIST_FILE_PATH="$1";shift 1
|
||||
else
|
||||
echo "Wrong path \"$1\" of list file.";exit 1
|
||||
fi
|
||||
@@ -541,6 +541,7 @@ if git remote get-url origin &>/dev/null; then
|
||||
log_info "Pulling changes from origin/$current_branch..."
|
||||
if git pull; then
|
||||
log_success "Successfully pulled latest changes"
|
||||
git submodule update --init --recursive
|
||||
else
|
||||
log_warning "Failed to pull changes from remote. Continuing with local repository..."
|
||||
log_info "You may need to resolve conflicts manually later."
|
||||
|
||||
@@ -844,6 +844,7 @@ if git remote get-url origin &>/dev/null; then
|
||||
else
|
||||
if git pull --ff-only; then
|
||||
log_success "Successfully pulled latest changes"
|
||||
git submodule update --init --recursive
|
||||
# Verify we actually got new commits
|
||||
if git rev-parse --verify HEAD@{1} &>/dev/null; then
|
||||
if [[ "$(git rev-parse HEAD)" == "$(git rev-parse HEAD@{1})" ]]; then
|
||||
|
||||
@@ -9,10 +9,8 @@ printf "${STY_CYAN}[$0]: Hi there! Before we start:${STY_RST}\n"
|
||||
printf "\n"
|
||||
printf "${STY_PURPLE}${STY_BOLD}[NEW] illogical-impulse is now powered by Quickshell.${STY_RST}\n"
|
||||
printf "${STY_PURPLE}"
|
||||
printf '# NOTE: illogical-impulse on AGS is no longer supported.\n'
|
||||
printf '# If you were using the old version with AGS and would like to keep it, do not run this script.\n'
|
||||
printf '# The AGS version, although uses less memory, has much worse performance (it uses Gtk3). \n'
|
||||
printf '# If you aren'\''t running on ewaste, the Quickshell version is recommended. \n'
|
||||
printf "# If you would like the AGS version anyway, run the following to switch to its branch first:\n ${STY_INVERT} git checkout ii-ags && ./install.sh ${STY_RST}\n"
|
||||
printf "\n"
|
||||
pause
|
||||
printf "${STY_CYAN}${STY_BOLD}Quick overview about what this script does:${STY_RST}\n"
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
# This script is meant to be sourced.
|
||||
# It's not for directly running.
|
||||
|
||||
# TODO: https://github.com/end-4/dots-hyprland/issues/2137
|
||||
|
||||
printf "${STY_CYAN}[$0]: 3. Copying config files (experimental YAML-based)${STY_RST}\n"
|
||||
|
||||
# Configuration file
|
||||
CONFIG_FILE="sdata/subcmd-install/3.files.yaml"
|
||||
|
||||
# =============================================================================
|
||||
# ORIGINAL FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
function warning_rsync_delete(){
|
||||
printf "${STY_YELLOW}"
|
||||
printf "The command below uses --delete for rsync which overwrites the destination folder.\n"
|
||||
printf "${STY_RST}"
|
||||
}
|
||||
|
||||
function warning_rsync_normal(){
|
||||
printf "${STY_YELLOW}"
|
||||
printf "The command below uses rsync which overwrites the destination.\n"
|
||||
printf "${STY_RST}"
|
||||
}
|
||||
|
||||
function backup_configs(){
|
||||
backup_clashing_targets dots/.config $XDG_CONFIG_HOME "${BACKUP_DIR}/.config"
|
||||
backup_clashing_targets dots/.local/share $XDG_DATA_HOME "${BACKUP_DIR}/.local/share"
|
||||
printf "${STY_BLUE}Backup into \"${BACKUP_DIR}\" finished.${STY_RST}\n"
|
||||
}
|
||||
|
||||
function ask_backup_configs(){
|
||||
showfun backup_clashing_targets
|
||||
printf "${STY_RED}"
|
||||
printf "Would you like to backup clashing dirs/files under \"$XDG_CONFIG_HOME\" and \"$XDG_DATA_HOME\" to \"$BACKUP_DIR\"?"
|
||||
printf "${STY_RST}"
|
||||
while true;do
|
||||
echo " y = Yes, backup"
|
||||
echo " n/s = No, skip to next"
|
||||
local p; read -p "====> " p
|
||||
case $p in
|
||||
[yY]) echo -e "${STY_BLUE}OK, doing backup...${STY_RST}" ;local backup=true;break ;;
|
||||
[nNsS]) echo -e "${STY_BLUE}Alright, skipping...${STY_RST}" ;local backup=false;break ;;
|
||||
*) echo -e "${STY_RED}Please enter [y/n].${STY_RST}";;
|
||||
esac
|
||||
done
|
||||
if $backup;then backup_configs;fi
|
||||
}
|
||||
function auto_backup_configs(){
|
||||
# Backup when $BACKUP_DIR does not exist
|
||||
if [[ ! -d "$BACKUP_DIR" ]]; then backup_configs;fi
|
||||
}
|
||||
|
||||
#####################################################################################
|
||||
showfun auto_get_git_submodule
|
||||
v auto_get_git_submodule
|
||||
|
||||
# In case some dirs does not exists
|
||||
v mkdir -p $XDG_BIN_HOME $XDG_CACHE_HOME $XDG_CONFIG_HOME $XDG_DATA_HOME/icons
|
||||
|
||||
if [[ ! "${SKIP_BACKUP}" == true ]]; then
|
||||
case $ask in
|
||||
false) auto_backup_configs ;;
|
||||
*) ask_backup_configs ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Run user preference wizard
|
||||
case $ask in
|
||||
false) sleep 0 ;;
|
||||
*) wizard_update_preferences ;;
|
||||
esac
|
||||
|
||||
# Read patterns from YAML file
|
||||
readarray patterns < <(yq -o=j -I=0 '.patterns[]' "$CONFIG_FILE")
|
||||
|
||||
# Process each pattern
|
||||
for pattern in "${patterns[@]}"; do
|
||||
from=$(echo "$pattern" | yq '.from' - | envsubst)
|
||||
to=$(echo "$pattern" | yq '.to' - | envsubst)
|
||||
mode=$(echo "$pattern" | yq '.mode' - | envsubst)
|
||||
condition=$(echo "$pattern" | yq '.condition // "true"')
|
||||
|
||||
# Handle fontconfig fontset override
|
||||
# If FONTSET_DIR_NAME is set and this is the fontconfig pattern, use the fontset instead
|
||||
if [[ "$from" == "dots/.config/fontconfig" ]] && [[ -n "${FONTSET_DIR_NAME:-}" ]]; then
|
||||
from="dots-extra/fontsets/${FONTSET_DIR_NAME}"
|
||||
echo "Using fontset \"${FONTSET_DIR_NAME}\" for fontconfig"
|
||||
fi
|
||||
|
||||
# Check if pattern should be processed
|
||||
if ! should_process_pattern "$pattern"; then
|
||||
# Format condition message nicely
|
||||
if [[ "$condition" != "true" ]]; then
|
||||
cond_type=$(echo "$condition" | yq -r '.type // ""')
|
||||
cond_value=$(echo "$condition" | yq -r '.value // ""')
|
||||
if [[ -n "$cond_type" && -n "$cond_value" ]]; then
|
||||
echo "Skipping $from -> $to (condition not met: $cond_type == '$cond_value')"
|
||||
else
|
||||
echo "Skipping $from -> $to (condition not met)"
|
||||
fi
|
||||
else
|
||||
echo "Skipping $from -> $to (condition not met)"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Processing: $from -> $to (mode: $mode)"
|
||||
|
||||
# Build exclude arguments for rsync
|
||||
excludes=()
|
||||
if echo "$pattern" | yq -e '.excludes' >/dev/null 2>&1; then
|
||||
while IFS= read -r exclude; do
|
||||
excludes+=(--exclude "$exclude")
|
||||
done < <(echo "$pattern" | yq -r '.excludes[]')
|
||||
fi
|
||||
|
||||
# Check if source exists
|
||||
if [[ ! -e "$from" ]]; then
|
||||
echo "Warning: Source does not exist: $from (skipping)"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Ensure destination directory exists for files
|
||||
if [[ -f "$from" ]]; then
|
||||
v mkdir -p "$(dirname "$to")"
|
||||
fi
|
||||
|
||||
# Execute based on mode
|
||||
case $mode in
|
||||
"sync")
|
||||
if [[ -d "$from" ]]; then
|
||||
warning_rsync_delete
|
||||
v rsync -av --delete "${excludes[@]}" "$from/" "$to/"
|
||||
else
|
||||
warning_rsync_normal
|
||||
# For files, don't use trailing slash and don't use --delete
|
||||
v rsync -av "${excludes[@]}" "$from" "$to"
|
||||
fi
|
||||
;;
|
||||
"soft")
|
||||
warning_rsync_normal
|
||||
if [[ -d "$from" ]]; then
|
||||
v rsync -av "${excludes[@]}" "$from/" "$to/"
|
||||
else
|
||||
# For files, don't use trailing slash
|
||||
v rsync -av "${excludes[@]}" "$from" "$to"
|
||||
fi
|
||||
;;
|
||||
"hard")
|
||||
v cp -r "$from" "$to"
|
||||
;;
|
||||
"hard-backup")
|
||||
if [[ -e "$to" ]]; then
|
||||
if files_are_same "$from" "$to"; then
|
||||
echo "Files are identical, skipping backup"
|
||||
else
|
||||
backup_number=$(get_next_backup_number "$to")
|
||||
v mv "$to" "$to.old.$backup_number"
|
||||
v cp -r "$from" "$to"
|
||||
fi
|
||||
else
|
||||
v cp -r "$from" "$to"
|
||||
fi
|
||||
;;
|
||||
"soft-backup")
|
||||
if [[ -e "$to" ]]; then
|
||||
if files_are_same "$from" "$to"; then
|
||||
echo "Files are identical, skipping backup"
|
||||
else
|
||||
v cp -r "$from" "$to.new"
|
||||
fi
|
||||
else
|
||||
v cp -r "$from" "$to"
|
||||
fi
|
||||
;;
|
||||
"skip")
|
||||
echo "Skipping $from"
|
||||
;;
|
||||
"skip-if-exists")
|
||||
if [[ -e "$to" ]]; then
|
||||
echo "Skipping $from (destination exists)"
|
||||
else
|
||||
v cp -r "$from" "$to"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Unknown mode: $mode"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Prevent hyprland from not fully loaded
|
||||
sleep 1
|
||||
try hyprctl reload
|
||||
|
||||
# Rest of original script logic...
|
||||
# (Keep the existing warning messages and file checks)
|
||||
|
||||
warn_files=()
|
||||
warn_files_tests=()
|
||||
warn_files_tests+=(/usr/local/lib/{GUtils-1.0.typelib,Gvc-1.0.typelib,libgutils.so,libgvc.so})
|
||||
warn_files_tests+=(/usr/local/share/fonts/TTF/Rubik{,-Italic}'[wght]'.ttf)
|
||||
warn_files_tests+=(/usr/local/share/licenses/ttf-rubik)
|
||||
warn_files_tests+=(/usr/local/share/fonts/TTF/Gabarito-{Black,Bold,ExtraBold,Medium,Regular,SemiBold}.ttf)
|
||||
warn_files_tests+=(/usr/local/share/licenses/ttf-gabarito)
|
||||
warn_files_tests+=(/usr/local/share/icons/OneUI{,-dark,-light})
|
||||
warn_files_tests+=(/usr/local/share/icons/Bibata-Modern-Classic)
|
||||
warn_files_tests+=(/usr/local/bin/{LaTeX,res})
|
||||
for i in ${warn_files_tests[@]}; do
|
||||
echo $i
|
||||
test -f $i && warn_files+=($i)
|
||||
test -d $i && warn_files+=($i)
|
||||
done
|
||||
|
||||
#####################################################################################
|
||||
# TODO: output the logs below to a temp file and cat that file, also show the path of the file so users will be able to read it again.
|
||||
printf "\n"
|
||||
printf "\n"
|
||||
printf "\n"
|
||||
printf "${STY_CYAN}[$0]: Finished${STY_RESET}\n"
|
||||
printf "\n"
|
||||
printf "${STY_CYAN}When starting Hyprland from your display manager (login screen) ${STY_RED} DO NOT SELECT UWSM ${STY_RESET}\n"
|
||||
printf "\n"
|
||||
printf "${STY_CYAN}If you are already running Hyprland,${STY_RESET}\n"
|
||||
printf "${STY_CYAN}Press ${STY_BG_CYAN} Ctrl+Super+T ${STY_BG_CYAN} to select a wallpaper${STY_RESET}\n"
|
||||
printf "${STY_CYAN}Press ${STY_BG_CYAN} Super+/ ${STY_CYAN} for a list of keybinds${STY_RESET}\n"
|
||||
printf "\n"
|
||||
printf "${STY_CYAN}For suggestions/hints after installation:${STY_RESET}\n"
|
||||
printf "${STY_CYAN}${STY_UNDERLINE} https://ii.clsty.link/en/ii-qs/01setup/#post-installation ${STY_RESET}\n"
|
||||
printf "\n"
|
||||
|
||||
if [[ -z "${ILLOGICAL_IMPULSE_VIRTUAL_ENV}" ]]; then
|
||||
printf "\n${STY_RED}[$0]: \!! Important \!! : Please ensure environment variable ${STY_RESET} \$ILLOGICAL_IMPULSE_VIRTUAL_ENV ${STY_RED} is set to proper value (by default \"~/.local/state/quickshell/.venv\"), or Quickshell config will not work. We have already provided this configuration in ~/.config/hypr/hyprland/env.conf, but you need to ensure it is included in hyprland.conf, and also a restart is needed for applying it.${STY_RESET}\n"
|
||||
fi
|
||||
|
||||
if [[ ! -z "${warn_files[@]}" ]]; then
|
||||
printf "\n${STY_RED}[$0]: \!! Important \!! : Please delete ${STY_RESET} ${warn_files[*]} ${STY_RED} manually as soon as possible, since we\'re now using AUR package or local PKGBUILD to install them for Arch(based) Linux distros, and they'll take precedence over our installation, or at least take up more space.${STY_RESET}\n"
|
||||
fi
|
||||
@@ -18,72 +18,32 @@ function warning_rsync_normal(){
|
||||
printf "${STY_RST}"
|
||||
}
|
||||
|
||||
function backup_clashing_targets(){
|
||||
# For dirs/files under target_dir, only backup those which clashes with the ones under source_dir
|
||||
|
||||
# Deal with arguments
|
||||
local source_dir="$1"
|
||||
local target_dir="$2"
|
||||
local backup_dir="$3"
|
||||
|
||||
# Find clash dirs/files, save as clash_list
|
||||
local clash_list=()
|
||||
local source_list=($(ls -A "$source_dir"))
|
||||
local target_list=($(ls -A "$target_dir"))
|
||||
declare -A target_map
|
||||
for i in "${target_list[@]}"; do
|
||||
target_map["$i"]=1
|
||||
done
|
||||
for i in "${source_list[@]}"; do
|
||||
if [[ -n "${target_map[$i]}" ]]; then
|
||||
clash_list+=("$i")
|
||||
fi
|
||||
done
|
||||
|
||||
# Construct args_includes for rsync
|
||||
local args_includes=()
|
||||
for i in "${clash_list[@]}"; do
|
||||
if [[ -d "$target_dir/$i" ]]; then
|
||||
args_includes+=(--include="/$i/")
|
||||
args_includes+=(--include="/$i/**")
|
||||
else
|
||||
args_includes+=(--include="/$i")
|
||||
fi
|
||||
done
|
||||
args_includes+=(--exclude='*')
|
||||
|
||||
x mkdir -p $backup_dir
|
||||
x rsync -av --progress "${args_includes[@]}" "$target_dir/" "$backup_dir/"
|
||||
function backup_configs(){
|
||||
backup_clashing_targets dots/.config $XDG_CONFIG_HOME "${BACKUP_DIR}/.config"
|
||||
backup_clashing_targets dots/.local/share $XDG_DATA_HOME "${BACKUP_DIR}/.local/share"
|
||||
printf "${STY_BLUE}Backup into \"${BACKUP_DIR}\" finished.${STY_RST}\n"
|
||||
}
|
||||
|
||||
function ask_backup_configs(){
|
||||
showfun backup_clashing_targets
|
||||
printf "${STY_RED}"
|
||||
printf "Would you like to backup clashing dirs/files under \"$XDG_CONFIG_HOME\" and \"$XDG_DATA_HOME\" to \"$BACKUP_DIR\"?"
|
||||
printf "Would you like to backup clashing dirs/files under \"$XDG_CONFIG_HOME\" and \"$XDG_DATA_HOME\" to \"$BACKUP_DIR\"?\n"
|
||||
printf "${STY_RST}"
|
||||
while true;do
|
||||
echo " y = Yes, backup"
|
||||
echo " n = No, skip to next"
|
||||
echo " n/s = No, skip to next"
|
||||
local p; read -p "====> " p
|
||||
case $p in
|
||||
[yY]) echo -e "${STY_BLUE}OK, doing backup...${STY_RST}" ;local backup=true;break ;;
|
||||
[nN]) echo -e "${STY_BLUE}Alright, skipping...${STY_RST}" ;local backup=false;break ;;
|
||||
[nNsS]) echo -e "${STY_BLUE}Alright, skipping...${STY_RST}" ;local backup=false;break ;;
|
||||
*) echo -e "${STY_RED}Please enter [y/n].${STY_RST}";;
|
||||
esac
|
||||
done
|
||||
if $backup;then
|
||||
backup_clashing_targets dots/.config $XDG_CONFIG_HOME "${BACKUP_DIR}/.config"
|
||||
backup_clashing_targets dots/.local/share $XDG_DATA_HOME "${BACKUP_DIR}/.local/share"
|
||||
printf "${STY_BLUE}Backup into \"${BACKUP_DIR}\" finished.${STY_RST}\n"
|
||||
fi
|
||||
if $backup;then backup_configs;fi
|
||||
}
|
||||
function auto_backup_configs(){
|
||||
# Backup when $BACKUP_DIR does not exist
|
||||
if [[ ! -d "$BACKUP_DIR" ]]; then
|
||||
backup_clashing_targets dots/.config $XDG_CONFIG_HOME "${BACKUP_DIR}/.config"
|
||||
backup_clashing_targets dots/.local/share $XDG_DATA_HOME "${BACKUP_DIR}/.local/share"
|
||||
printf "${STY_BLUE}Backup into \"${BACKUP_DIR}\" finished.${STY_RST}\n"
|
||||
fi
|
||||
if [[ ! -d "$BACKUP_DIR" ]]; then backup_configs;fi
|
||||
}
|
||||
|
||||
#####################################################################################
|
||||
@@ -143,9 +103,9 @@ esac
|
||||
case $SKIP_FONTCONFIG in
|
||||
true) sleep 0;;
|
||||
*)
|
||||
case "$II_FONTSET_NAME" in
|
||||
case "$FONTSET_DIR_NAME" in
|
||||
"") warning_rsync_delete; v rsync -av --delete dots/.config/fontconfig/ "$XDG_CONFIG_HOME"/fontconfig/ ;;
|
||||
*) warning_rsync_delete; v rsync -av --delete dots-extra/fontsets/$II_FONTSET_NAME/ "$XDG_CONFIG_HOME"/fontconfig/ ;;
|
||||
*) warning_rsync_delete; v rsync -av --delete dots-extra/fontsets/$FONTSET_DIR_NAME/ "$XDG_CONFIG_HOME"/fontconfig/ ;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
@@ -159,16 +119,24 @@ case $SKIP_HYPRLAND in
|
||||
true) sleep 0;;
|
||||
*)
|
||||
warning_rsync_delete; v rsync -av --delete "${arg_excludes[@]}" dots/.config/hypr/ "$XDG_CONFIG_HOME"/hypr/
|
||||
# When hypr/custom does not exist, we assume that it's the firstrun.
|
||||
if [ -d "$XDG_CONFIG_HOME/hypr/custom" ];then ii_firstrun=false;else ii_firstrun=true;fi
|
||||
t="$XDG_CONFIG_HOME/hypr/hyprland.conf"
|
||||
if [ -f $t ];then
|
||||
echo -e "${STY_BLUE}[$0]: \"$t\" already exists.${STY_RST}"
|
||||
v mv $t $t.old
|
||||
v cp -f dots/.config/hypr/hyprland.conf $t
|
||||
existed_hypr_conf_firstrun=y
|
||||
if $ii_firstrun;then
|
||||
echo -e "${STY_BLUE}[$0]: It seems to be the firstrun.${STY_RST}"
|
||||
v mv $t $t.old
|
||||
v cp -f dots/.config/hypr/hyprland.conf $t
|
||||
existed_hypr_conf_firstrun=y
|
||||
else
|
||||
echo -e "${STY_BLUE}[$0]: It seems not a firstrun.${STY_RST}"
|
||||
v cp -f dots/.config/hypr/hyprland.conf $t.new
|
||||
existed_hypr_conf=y
|
||||
fi
|
||||
else
|
||||
echo -e "${STY_YELLOW}[$0]: \"$t\" does not exist yet.${STY_RST}"
|
||||
v cp dots/.config/hypr/hyprland.conf $t
|
||||
existed_hypr_conf=n
|
||||
fi
|
||||
t="$XDG_CONFIG_HOME/hypr/hypridle.conf"
|
||||
if [ -f $t ];then
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
version: "1.0"
|
||||
user_preferences:
|
||||
shell: "fish" # fish | zsh
|
||||
terminal: "foot" # kitty | foot
|
||||
keybindings: "default" # default | vim
|
||||
patterns:
|
||||
# Always install these files
|
||||
- from: "dots/.config/quickshell"
|
||||
to: "$XDG_CONFIG_HOME/quickshell"
|
||||
mode: "sync"
|
||||
# Conditionally install these files
|
||||
- from: "dots/.config/fish"
|
||||
to: "$XDG_CONFIG_HOME/fish"
|
||||
mode: "sync"
|
||||
condition:
|
||||
type: "shell"
|
||||
value: "fish"
|
||||
- from: "dots/.config/zshrc.d"
|
||||
to: "$XDG_CONFIG_HOME/zshrc.d"
|
||||
mode: "sync"
|
||||
condition:
|
||||
type: "shell"
|
||||
value: "zsh"
|
||||
- from: "dots/.config/foot"
|
||||
to: "$XDG_CONFIG_HOME/foot"
|
||||
mode: "sync"
|
||||
condition:
|
||||
type: "terminal"
|
||||
value: "foot"
|
||||
- from: "dots/.config/kitty"
|
||||
to: "$XDG_CONFIG_HOME/kitty"
|
||||
mode: "sync"
|
||||
condition:
|
||||
type: "terminal"
|
||||
value: "kitty"
|
||||
# Hyprland
|
||||
- from: "dots/.config/hypr"
|
||||
to: "$XDG_CONFIG_HOME/hypr"
|
||||
mode: "sync"
|
||||
excludes: ["custom", "hyprlock.conf", "hypridle.conf", "hyprland.conf"]
|
||||
# Hyprland special files
|
||||
- from: "dots/.config/hypr/hyprland.conf"
|
||||
to: "$XDG_CONFIG_HOME/hypr/hyprland.conf"
|
||||
mode: "hard-backup"
|
||||
- from: "dots/.config/hypr/hypridle.conf"
|
||||
to: "$XDG_CONFIG_HOME/hypr/hypridle.conf"
|
||||
mode: "soft-backup"
|
||||
- from: "dots/.config/hypr/hyprlock.conf"
|
||||
to: "$XDG_CONFIG_HOME/hypr/hyprlock.conf"
|
||||
mode: "soft-backup"
|
||||
- from: "dots/.config/hypr/custom"
|
||||
to: "$XDG_CONFIG_HOME/hypr/custom"
|
||||
mode: "skip-if-exists"
|
||||
- from: "dots/.local/share/icons"
|
||||
to: "$XDG_DATA_HOME/icons"
|
||||
mode: "soft"
|
||||
- from: "dots/.local/share/konsole"
|
||||
to: "$XDG_DATA_HOME/konsole"
|
||||
mode: "soft"
|
||||
# Fontconfig (default - fontsets handled separately if FONTSET_DIR_NAME is set)
|
||||
- from: "dots/.config/fontconfig"
|
||||
to: "$XDG_CONFIG_HOME/fontconfig"
|
||||
mode: "sync"
|
||||
# MISC config directories (other .config directories)
|
||||
- from: "dots/.config/fuzzel"
|
||||
to: "$XDG_CONFIG_HOME/fuzzel"
|
||||
mode: "sync"
|
||||
- from: "dots/.config/kde-material-you-colors"
|
||||
to: "$XDG_CONFIG_HOME/kde-material-you-colors"
|
||||
mode: "sync"
|
||||
- from: "dots/.config/Kvantum"
|
||||
to: "$XDG_CONFIG_HOME/Kvantum"
|
||||
mode: "sync"
|
||||
- from: "dots/.config/matugen"
|
||||
to: "$XDG_CONFIG_HOME/matugen"
|
||||
mode: "sync"
|
||||
- from: "dots/.config/mpv"
|
||||
to: "$XDG_CONFIG_HOME/mpv"
|
||||
mode: "sync"
|
||||
- from: "dots/.config/qt5ct"
|
||||
to: "$XDG_CONFIG_HOME/qt5ct"
|
||||
mode: "sync"
|
||||
- from: "dots/.config/qt6ct"
|
||||
to: "$XDG_CONFIG_HOME/qt6ct"
|
||||
mode: "sync"
|
||||
- from: "dots/.config/wlogout"
|
||||
to: "$XDG_CONFIG_HOME/wlogout"
|
||||
mode: "sync"
|
||||
- from: "dots/.config/xdg-desktop-portal"
|
||||
to: "$XDG_CONFIG_HOME/xdg-desktop-portal"
|
||||
mode: "sync"
|
||||
# MISC config files (individual files in .config)
|
||||
- from: "dots/.config/chrome-flags.conf"
|
||||
to: "$XDG_CONFIG_HOME/chrome-flags.conf"
|
||||
mode: "soft"
|
||||
- from: "dots/.config/code-flags.conf"
|
||||
to: "$XDG_CONFIG_HOME/code-flags.conf"
|
||||
mode: "soft"
|
||||
- from: "dots/.config/darklyrc"
|
||||
to: "$XDG_CONFIG_HOME/darklyrc"
|
||||
mode: "soft"
|
||||
- from: "dots/.config/dolphinrc"
|
||||
to: "$XDG_CONFIG_HOME/dolphinrc"
|
||||
mode: "soft"
|
||||
- from: "dots/.config/kdeglobals"
|
||||
to: "$XDG_CONFIG_HOME/kdeglobals"
|
||||
mode: "soft"
|
||||
- from: "dots/.config/konsolerc"
|
||||
to: "$XDG_CONFIG_HOME/konsolerc"
|
||||
mode: "soft"
|
||||
- from: "dots/.config/starship.toml"
|
||||
to: "$XDG_CONFIG_HOME/starship.toml"
|
||||
mode: "soft"
|
||||
- from: "dots/.config/thorium-flags.conf"
|
||||
to: "$XDG_CONFIG_HOME/thorium-flags.conf"
|
||||
mode: "soft"
|
||||
@@ -1,7 +1,7 @@
|
||||
# Handle args for subcmd: install
|
||||
# shellcheck shell=bash
|
||||
showhelp(){
|
||||
echo -e "Syntax: $0 install [OPTIONS]...
|
||||
printf "Syntax: $0 install [OPTIONS]...
|
||||
|
||||
Idempotent installation for dotfiles.
|
||||
|
||||
@@ -23,11 +23,17 @@ Options for install:
|
||||
--skip-miscconf Skip copying the dirs and files to \".configs\" except for
|
||||
Quickshell, Fish and Hyprland
|
||||
--core Alias of --skip-{plasmaintg,fish,miscconf,fontconfig}
|
||||
--exp-files Use experimental script for the third step copying files
|
||||
--fontset <set> Use a set of pre-defined font and config (currently only fontconfig).
|
||||
Possible values of <set>: $(ls -A ${REPO_ROOT}/dots-extra/fontsets)
|
||||
--via-nix (Unavailable yet) Use Nix to install dependencies
|
||||
"
|
||||
${STY_CYAN}
|
||||
New features (experimental):
|
||||
--exp-files Use yaml-based config for the third step copying files.
|
||||
This feature is ${STY_YELLOW}still on early stage${STY_CYAN}, feedback and contribution welcomed,
|
||||
see https://github.com/end-4/dots-hyprland/issues/2137 for details.
|
||||
--via-nix Use Nix and Home-manager to install dependencies.
|
||||
This feature is ${STY_RED}working in progress${STY_CYAN}. Contribution is welcomed,
|
||||
see https://github.com/end-4/dots-hyprland/issues/1061 for details.
|
||||
${STY_RST}"
|
||||
}
|
||||
|
||||
cleancache(){
|
||||
@@ -81,7 +87,7 @@ while true ; do
|
||||
## Ones with parameter
|
||||
--fontset)
|
||||
if [[ -d "${REPO_ROOT}/dots-extra/fontsets/$2" ]];
|
||||
then echo "Using fontset \"$2\".";II_FONTSET_NAME="$2";shift 2
|
||||
then echo "Using fontset \"$2\".";FONTSET_DIR_NAME="$2";shift 2
|
||||
else echo "Wrong argument for $1.";exit 1
|
||||
fi;;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ Subcommands:
|
||||
exp-uninstall (Experimental) Uninstall illogical-impulse.
|
||||
exp-update (Experimental) Update illogical-impulse without fully reinstall.
|
||||
exp-update-old (Experimental) exp-update but use behaves like old version.
|
||||
checkdeps (For dev only) Check whether pkgs exist in AUR or repos of Arch.
|
||||
help Show this help message.
|
||||
|
||||
For each <subcommand>, use -h for details:
|
||||
@@ -34,7 +35,7 @@ case $1 in
|
||||
# Global help
|
||||
help|--help|-h)showhelp_global;exit;;
|
||||
# Correct subcommand
|
||||
install|exp-uninstall|exp-update|exp-update-old)
|
||||
install|exp-uninstall|exp-update|exp-update-old|checkdeps)
|
||||
SUBCMD_NAME=$1
|
||||
SUBCMD_DIR=./sdata/subcmd-$1
|
||||
shift;;
|
||||
|
||||
Reference in New Issue
Block a user