qs: update

This commit is contained in:
2026-03-08 03:30:05 +01:00
parent ad6486c141
commit 0d13a02e29
11 changed files with 196 additions and 141 deletions
+2 -1
View File
@@ -13,7 +13,8 @@ binds {
Mod+C { spawn "code"; } Mod+C { spawn "code"; }
Mod+E { spawn "dolphin" "--new-window"; } Mod+E { spawn "dolphin" "--new-window"; }
Mod+W { spawn-sh "zen || zen-browser"; } Mod+W { spawn-sh "zen || zen-browser"; }
Mod+X { spawn "gnome-text-editor" "--new-window"; } Mod+X { spawn "qs" "ipc" "call" "notes" "openRecent"; }
Mod+Shift+X { spawn "qs" "ipc" "call" "notes" "createNote"; }
Mod+B { spawn-sh "pkill -x -n btop || wezterm -e btop"; } Mod+B { spawn-sh "pkill -x -n btop || wezterm -e btop"; }
Mod+Shift+T { spawn "wezterm"; } Mod+Shift+T { spawn "wezterm"; }
Mod+Shift+Return { spawn "wezterm"; } Mod+Shift+Return { spawn "wezterm"; }
@@ -72,18 +72,18 @@ Variants {
RowLayout { RowLayout {
id: leftLayout id: leftLayout
height: parent.height - 10 height: parent.height - Style.marginXS * 2
anchors { anchors {
left: parent.left left: parent.left
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
leftMargin: 5 leftMargin: Style.marginXS
} }
UIconButton { UIconButton {
textOverride: "󰣇" textOverride: "󰣇"
fontFamily: Fonts.nerd fontFamily: Fonts.nerd
baseSize: parent.height - Style.marginXXS * 2 baseSize: parent.height - Style.marginXS * 2
iconSize: Style.fontNerd iconSize: Style.fontNerd
colorFg: Colors.distro colorFg: Colors.distro
onClicked: () => { onClicked: () => {
@@ -121,7 +121,7 @@ Variants {
RowLayout { RowLayout {
id: middleLayout id: middleLayout
height: parent.height - 10 height: parent.height - Style.marginXS * 2
anchors { anchors {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
@@ -136,27 +136,46 @@ Variants {
RowLayout { RowLayout {
id: rightLayout id: rightLayout
height: parent.height - 10 height: parent.height - Style.marginXS * 2
anchors { anchors {
right: parent.right right: parent.right
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
rightMargin: 5 rightMargin: Style.marginXS
} }
Loader { Connections {
sourceComponent: LyricsService.showLyricsBar ? lyricsComponent : monitorsComponent target: LyricsService
onShowLyricsBarChanged: {
if (LyricsService.showLyricsBar) {
LyricsService.registerComponent("LyricsBar");
SystemStatService.unregisterComponent("BarMonitors");
} else {
LyricsService.unregisterComponent("LyricsBar");
SystemStatService.registerComponent("BarMonitors");
}
}
}
Component { Item {
id: monitorsComponent Layout.fillHeight: true
Layout.fillWidth: true
RowLayout { RowLayout {
id: monitorsLayout id: monitorsLayout
height: rightLayout.height anchors.right: parent.right
height: parent.height
y: LyricsService.showLyricsBar ? Style.barHeight : 0
opacity: LyricsService.showLyricsBar ? 0 : 1
spacing: Style.marginM spacing: Style.marginM
Component.onCompleted: { Component.onCompleted: {
if (!LyricsService.showLyricsBar)
SystemStatService.registerComponent("BarMonitors"); SystemStatService.registerComponent("BarMonitors");
}
Component.onDestruction: {
SystemStatService.unregisterComponent("BarMonitors");
} }
NetworkSpeed { NetworkSpeed {
@@ -190,14 +209,54 @@ Variants {
Volume { Volume {
} }
Behavior on y {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
} }
} }
Component { Behavior on opacity {
id: lyricsComponent NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
}
LyricsBar { LyricsBar {
id: lyricsBar
anchors.right: parent.right
height: parent.height
y: LyricsService.showLyricsBar ? 0 : -Style.barHeight
opacity: LyricsService.showLyricsBar ? 1 : 0
Component.onCompleted: {
if (LyricsService.showLyricsBar)
LyricsService.registerComponent("LyricsBar");
}
Component.onDestruction: {
LyricsService.unregisterComponent("LyricsBar");
}
Behavior on y {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
Behavior on opacity {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
} }
} }
@@ -208,18 +267,18 @@ Variants {
} }
RowLayout { RowLayout {
height: rightLayout.height Layout.fillHeight: true
spacing: Style.marginS spacing: Style.marginS
TrayExpander { TrayExpander {
screen: modelData screen: modelData
baseSize: rightLayout.height - Style.marginXXS * 2 baseSize: rightLayout.height - Style.marginXS * 2
} }
UIconButton { UIconButton {
iconName: Caffeine.isInhibited ? "mug-off" : "mug" iconName: Caffeine.isInhibited ? "mug-off" : "mug"
colorFg: Caffeine.isInhibited ? Colors.mOrange : Colors.mYellow colorFg: Caffeine.isInhibited ? Colors.mOrange : Colors.mYellow
baseSize: rightLayout.height - Style.marginXXS * 2 baseSize: rightLayout.height - Style.marginXS * 2
alwaysHover: Caffeine.isInhibited alwaysHover: Caffeine.isInhibited
onClicked: () => { onClicked: () => {
Caffeine.manualToggle(); Caffeine.manualToggle();
@@ -229,7 +288,7 @@ Variants {
UIconButton { UIconButton {
iconName: "power" iconName: "power"
colorFg: Colors.mRed colorFg: Colors.mRed
baseSize: rightLayout.height - Style.marginXXS * 2 baseSize: rightLayout.height - Style.marginXS * 2
onClicked: () => { onClicked: () => {
BarService.toggleRight(); BarService.toggleRight();
} }
@@ -10,12 +10,6 @@ Rectangle {
color: Colors.mSurface color: Colors.mSurface
border.color: Colors.mPrimary border.color: Colors.mPrimary
border.width: Style.borderS border.width: Style.borderS
Component.onCompleted: {
LyricsService.startSyncing();
}
Component.onDestruction: {
LyricsService.stopSyncing();
}
implicitHeight: Style.barHeight - Style.marginXS * 2 implicitHeight: Style.barHeight - Style.marginXS * 2
implicitWidth: 600 implicitWidth: 600
@@ -148,14 +148,12 @@ UBox {
Layout.fillHeight: true Layout.fillHeight: true
clip: true clip: true
Loader { BluetoothCard {
id: btContentLoader
width: parent.width width: parent.width
height: parent.height height: parent.height
x: root.currentPanel === "bluetooth" ? 0 : -width x: root.currentPanel === "bluetooth" ? 0 : -width
opacity: root.currentPanel === "bluetooth" ? 1 : 0 opacity: root.currentPanel === "bluetooth" ? 1 : 0
sourceComponent: bluetoothComponent anchors.margins: Style.marginS
Behavior on x { Behavior on x {
NumberAnimation { NumberAnimation {
@@ -175,14 +173,12 @@ UBox {
} }
Loader { WifiCard {
id: wifiContentLoader
width: parent.width width: parent.width
height: parent.height height: parent.height
x: root.currentPanel === "wifi" ? 0 : width x: root.currentPanel === "wifi" ? 0 : width
opacity: root.currentPanel === "wifi" ? 1 : 0 opacity: root.currentPanel === "wifi" ? 1 : 0
sourceComponent: wifiComponent anchors.margins: Style.marginS
Behavior on x { Behavior on x {
NumberAnimation { NumberAnimation {
@@ -202,26 +198,6 @@ UBox {
} }
Component {
id: bluetoothComponent
BluetoothCard {
anchors.fill: parent
anchors.margins: Style.marginS
}
}
Component {
id: wifiComponent
WifiCard {
anchors.fill: parent
anchors.margins: Style.marginS
}
}
} }
} }
@@ -10,10 +10,10 @@ UBox {
id: lyricsBox id: lyricsBox
Component.onCompleted: { Component.onCompleted: {
LyricsService.startSyncing(); LyricsService.registerComponent("LyricsCard");
} }
Component.onDestruction: { Component.onDestruction: {
LyricsService.stopSyncing(); LyricsService.unregisterComponent("LyricsCard");
} }
ColumnLayout { ColumnLayout {
@@ -129,4 +129,16 @@ Item {
target: "brightness" target: "brightness"
} }
IpcHandler {
function openRecent() {
NotesService.openRecent();
}
function createNote() {
NotesService.createNote();
}
target: "notes"
}
} }
@@ -15,7 +15,10 @@ Singleton {
readonly property string offsetFile: Paths.cacheDir + "/spotify-lyrics-offset.txt" readonly property string offsetFile: Paths.cacheDir + "/spotify-lyrics-offset.txt"
property int offset: 0 // in ms property int offset: 0 // in ms
readonly property int offsetStep: 500 // in ms readonly property int offsetStep: 500 // in ms
property int referenceCount: 0 property var _registered: ({
})
readonly property int _registeredCount: Object.keys(_registered).length
readonly property bool shouldRun: _registeredCount > 0
// with linesCount=3 and linesAhead=1, lyrics will be like: // with linesCount=3 and linesAhead=1, lyrics will be like:
// line 1 // line 1
// line 2 <- current line // line 2 <- current line
@@ -30,29 +33,35 @@ Singleton {
} }
function startSyncing() { function startSyncing() {
referenceCount++; Logger.d("Lyrics", "Starting lyrics syncing");
Logger.d("LyricsService", "Reference count:", referenceCount);
if (referenceCount === 1) {
Logger.d("LyricsService", "Starting lyrics syncing");
// fill lyrics with empty lines // fill lyrics with empty lines
lyrics = Array(linesCount).fill(" "); lyrics = Array(linesCount).fill(" ");
listenProcess.exec(["sh", "-c", `pkill -x spotify-lyrics -u $USER; spotify-lyrics listen -l ${linesCount} -a ${linesAhead} -f ${offsetFile}`]); listenProcess.exec(["sh", "-c", `pkill -x spotify-lyrics -u $USER; spotify-lyrics listen -l ${linesCount} -a ${linesAhead} -f ${offsetFile}`]);
} }
}
function stopSyncing() { function stopSyncing() {
referenceCount--; Logger.d("Lyrics", "Stopping lyrics syncing");
Logger.d("LyricsService", "Reference count:", referenceCount);
if (referenceCount <= 0) {
Logger.d("LyricsService", "Stopping lyrics syncing");
// kinda ugly but works, meanwhile: // kinda ugly but works, meanwhile:
// listenProcess.signal(9) // listenProcess.signal(9)
// listenProcess.signal(15) // listenProcess.signal(15)
// listenProcess.running = false // listenProcess.running = false
// counting on exec() to terminate previous exec() // counting on exec() to terminate previous exec()
// all don't work // all don't work
listenProcess.exec(["sh", "-c", `pkill -x spotify-lyrics -u $USER`]); Quickshell.execDetached(["sh", "-c", `pkill -x spotify-lyrics -u $USER`]);
} }
function registerComponent(componentId) {
root._registered[componentId] = true;
root._registered = Object.assign({
}, root._registered);
Logger.d("Lyrics", "Component registered:", componentId, "- total:", root._registeredCount);
}
function unregisterComponent(componentId) {
delete root._registered[componentId];
root._registered = Object.assign({
}, root._registered);
Logger.d("Lyrics", "Component unregistered:", componentId, "- total:", root._registeredCount);
} }
function writeOffset() { function writeOffset() {
@@ -90,6 +99,12 @@ Singleton {
writeOffset(); writeOffset();
} }
onShouldRunChanged: {
if (shouldRun)
startSyncing();
else
stopSyncing();
}
Process { Process {
id: listenProcess id: listenProcess
@@ -129,7 +144,7 @@ Singleton {
const val = parseInt(fileContents); const val = parseInt(fileContents);
if (!isNaN(val)) { if (!isNaN(val)) {
offset = val; offset = val;
Logger.d("LyricsService", "Loaded offset:", offset); Logger.d("Lyrics", "Loaded offset:", offset);
} else { } else {
offset = 0; offset = 0;
writeOffset(); writeOffset();
@@ -139,14 +154,14 @@ Singleton {
writeOffset(); writeOffset();
} }
} catch (e) { } catch (e) {
Logger.e("LyricsService", "Error reading offset file:", e); Logger.e("Lyrics", "Error reading offset file:", e);
} }
} }
onLoadFailed: { onLoadFailed: {
Logger.e("LyricsService", "Error loading offset file."); Logger.e("Lyrics", "Error loading offset file.");
} }
onSaveFailed: { onSaveFailed: {
Logger.e("LyricsService", "Error saving offset file."); Logger.e("Lyrics", "Error saving offset file.");
} }
} }
@@ -9,6 +9,7 @@ Singleton {
id: root id: root
property ListModel notesModel property ListModel notesModel
property string recentNotePath: ""
function createNote() { function createNote() {
if (createProcess.running) { if (createProcess.running) {
@@ -36,9 +37,17 @@ Singleton {
} }
function openNote(path) { function openNote(path) {
recentNotePath = path;
Quickshell.execDetached(["wezterm", "start", "--", "sh", "-c", `exec nvim "${path}"`]); Quickshell.execDetached(["wezterm", "start", "--", "sh", "-c", `exec nvim "${path}"`]);
} }
function openRecent() {
if (recentNotePath)
openNote(recentNotePath);
else
createNote();
}
function strToColor(str) { function strToColor(str) {
let hash = 5381; let hash = 5381;
for (let i = 0; i < str.length; i++) { for (let i = 0; i < str.length; i++) {
@@ -86,6 +95,12 @@ Singleton {
break; break;
} }
} }
if (recentNotePath === currentPath)
if (notesModel.count > 0)
recentNotePath = notesModel.get(0).notePath;
else
recentNotePath = "";;
currentPath = ""; currentPath = "";
} }
} }
@@ -94,7 +109,7 @@ Singleton {
id: initProcess id: initProcess
running: true running: true
command: ["sh", "-c", "ls -p -tr " + Paths.notesDir] command: ["sh", "-c", "ls -p -t " + Paths.notesDir]
stdout: StdioCollector { stdout: StdioCollector {
id: listCollector id: listCollector
@@ -113,6 +128,9 @@ Singleton {
}); });
Logger.d("Notes", "Loaded note: " + fileName); Logger.d("Notes", "Loaded note: " + fileName);
} }
if (notesModel.count > 0)
recentNotePath = notesModel.get(0).notePath;
} }
} }
+2 -13
View File
@@ -1,14 +1,3 @@
#!/usr/bin/env bash #!/bin/sh
# Description: wezterm start -- fzfclip "$@"
# Wrapper for fzfclip to ensure only one instance is
# running and to launch it in terminal.
exec {LOCK_FD}>/tmp/"$(basename "$0")".lock
flock -n "$LOCK_FD" || {
echo "Another instance is running. Exiting." >&2
exit 1
}
wezterm start -- fzfclip "$@" {LOCK_FD}>&-
@@ -17,7 +17,7 @@
}, },
"action": { "action": {
"onSelected": "qs ipc call background setWallpaper '{{ path }}'; change-colortheme -c '{{ colorHex }}'", "onSelected": "qs ipc call background setWallpaper '{{ path }}'; change-colortheme -c '{{ colorHex }}'",
"onPreview": "qs ipc call background previewWallpaper '{{ path }}'; change-colortheme -c '{{ colorHex }}' quickshell niri", "onPreview": "qs ipc call background previewWallpaper '{{ path }}'; touch '{{ path }}'; change-colortheme -c '{{ colorHex }}' quickshell niri",
"quitOnSelected": true, "quitOnSelected": true,
"saveState": [ "saveState": [
{ {
+5 -14
View File
@@ -145,20 +145,7 @@
- [7.4. Entering the Chroot Environment](https://www.linuxfromscratch.org/lfs/view/stable/chapter07/chroot.html) - [7.4. Entering the Chroot Environment](https://www.linuxfromscratch.org/lfs/view/stable/chapter07/chroot.html)
如果想要使用 `arch-install-scripts` 提供的 `arch-chroot` 脚本偷懒,则必须确保环境变量的清洗与重新设置,至少应确保 `PATH`, `MAKEFLAGS` 和 `TESTSUITEFLAGS` 被正确设置 完全按照 LFS 书中的 chroot 步骤编写一个小脚本
```bash
arch-chroot "$LFS" /usr/bin/env -i \
HOME=/root \
TERM="$TERM" \
PS1='(lfs chroot) \u:\w\$ ' \
PATH=/usr/local/bin:/usr/bin:/usr/sbin \
MAKEFLAGS="-j$(nproc)" \
TESTSUITEFLAGS="-j$(nproc)" \
/bin/bash --login
```
或者完全按照 LFS 书中的 chroot 步骤编写一个小脚本:
```bash ```bash
#!/bin/bash #!/bin/bash
@@ -206,6 +193,10 @@
它的作用是自动挂载一系列虚拟文件系统,同时在退出 chroot 时自动清理。 它的作用是自动挂载一系列虚拟文件系统,同时在退出 chroot 时自动清理。
> [!IMPORTANT]
>
> 不建议使用 `arch-install-scripts` 提供的 `arch-chroot` 脚本偷懒, 即使用的话也需要做一些修改, 如对 `/dev` 不同的处理方式与默认缺失的环境变量.
- [8.64. GRUB-2.12](https://www.linuxfromscratch.org/lfs/view/stable/chapter08/grub.html) - [8.64. GRUB-2.12](https://www.linuxfromscratch.org/lfs/view/stable/chapter08/grub.html)
对于 UEFI 引导的系统,此时需要跳转 BLFS 安装 GRUB。为避免过早地陷入依赖地狱建议仅按照顺序安装以下包 对于 UEFI 引导的系统,此时需要跳转 BLFS 安装 GRUB。为避免过早地陷入依赖地狱建议仅按照顺序安装以下包