Krolyxon
Commit b7d60e8

Branches

Refs →

aurorab7d60e8

aurora: nothing os redesign, phase 4 (overlays)

Power menu as big Doto words with an uptime ring; runner, clipboard and glyph picker as
glyph-style cards over a dimmed screen (search pill with live caret, chips, round icons);
overview with Doto workspace numbers; polkit/bt prompt with a dot ring and dotted password
pill; dock as a pill of round icons; slurp themed for region recording and OCR.
Author
krolyxon
Date
2026-10-28 20:41 +0530
Parent
a8f0e9d
Commit
b7d60e8c3019c4860ba46e3785f5728e37d6a31d

+1038 −801

10 files changed

  1. M .config/quickshell/aurora/AuthPrompt.qml +121 −86
  2. M .config/quickshell/aurora/CharPicker.qml +148 −197
  3. M .config/quickshell/aurora/ClipWindow.qml +300 −162
  4. M .config/quickshell/aurora/Dock.qml +37 −24
  5. M .config/quickshell/aurora/Overview.qml +64 −39
  6. M .config/quickshell/aurora/PowerMenu.qml +126 −117
  7. M .config/quickshell/aurora/Rec.qml +1 −1
  8. M .config/quickshell/aurora/Runner.qml +235 −174
  9. M .config/quickshell/aurora/Shot.qml +1 −1
  10. M .config/quickshell/aurora/Theme.qml +5 −0
M .config/quickshell/aurora/AuthPrompt.qml View
@@ -7,6 +7,8 @@ import Quickshell.Wayland
77 // (Agents.bt: confirm a passkey, type a PIN/passkey, or show one to type on the device). Polkit wins
88 // if both are pending. Overlay layer with exclusive keyboard on the focused monitor; Enter accepts,
99 // Esc cancels. Typed text is cleared after every attempt, never logged.
10+// Looks like glyph's lock surface (github.com/Nebelung-cloud/glyph-rice, GPL-3.0, lock/LockSurface.qml): a lock in a ring
11+// of dots, the password in a pill as dots with the live caret; a wrong password shakes it.
1012 PanelWindow {
1113 id: win
1214 readonly property var flow: Agents.flow
@@ -21,8 +23,8 @@ PanelWindow {
2123 readonly property bool needsInput: mode === "polkit" || btInput
2224
2325 visible: opened
24− implicitWidth: 420
25− implicitHeight: col.implicitHeight + 32
26+ implicitWidth: 480
27+ implicitHeight: col.implicitHeight + 60
2628 exclusiveZone: 0
2729 color: "transparent"
2830 WlrLayershell.layer: WlrLayer.Overlay
@@ -56,7 +58,7 @@ PanelWindow {
5658 target: win.flow
5759 function onIsResponseRequiredChanged() { if (win.flow.isResponseRequired) win.reset() }
5860 // polkit asks again right after a failure (isResponseRequired goes true with failed set)
59− function onAuthenticationFailed() { win.error = "wrong password, try again"; win.reset() }
61+ function onAuthenticationFailed() { win.error = "wrong password, try again"; shake.restart(); win.reset() }
6062 }
6163
6264 // keyboard for the button-only bluetooth prompts
@@ -67,77 +69,91 @@ PanelWindow {
6769 Keys.onEscapePressed: win.cancel()
6870 }
6971
72+ readonly property bool bad: win.error !== "" || (win.mode === "polkit" && win.flow && win.flow.supplementaryIsError)
73+
7074 Rectangle {
7175 anchors.fill: parent
72− color: Theme.bgA
73− border.width: 2
74− border.color: win.error !== "" || (win.mode === "polkit" && win.flow && win.flow.supplementaryIsError) ? Theme.err : Theme.accent
76+ radius: 36
77+ color: Theme.s1
78+ border.width: 1
79+ border.color: win.bad ? Theme.err : Theme.s2
7580
7681 ColumnLayout {
7782 id: col
7883 anchors.left: parent.left
7984 anchors.right: parent.right
8085 anchors.top: parent.top
81− anchors.margins: 16
82− spacing: 10
86+ anchors.margins: 30
87+ spacing: 14
8388
8489 RowLayout {
8590 Layout.fillWidth: true
86− spacing: 10
87− Text {
88− text: win.mode === "bt" ? Theme.iBt : Theme.iLock
89− color: Theme.accent
90− font.family: Theme.font
91− font.pixelSize: Theme.fontSize + 8
91+ spacing: 16
92+ Item { // the lock / bluetooth glyph in a ring of dots
93+ width: 64
94+ height: 64
95+ Ring { anchors.fill: parent; anchors.margins: 3; n: 24; dot: 3.5; on: win.bad ? Theme.err : Theme.fill }
96+ Icon {
97+ anchors.centerIn: parent
98+ text: win.mode === "bt" ? Theme.iBt : Theme.iLock
99+ font.pixelSize: 24
100+ color: win.bad ? Theme.err : Theme.fg
101+ }
92102 }
93− SectionLabel {
103+ Column {
94104 Layout.fillWidth: true
95− text: win.mode === "bt" ? "bluetooth pairing" : "authentication required"
105+ spacing: 4
106+ Lb { text: win.mode === "bt" ? "bluetooth pairing" : "authentication" }
107+ Text {
108+ width: parent.width
109+ text: win.mode === "bt" ? (win.bt ? win.bt.alias : "") : "Required"
110+ elide: Text.ElideRight
111+ color: Theme.fg
112+ font.family: Theme.ui
113+ font.pixelSize: 20
114+ font.weight: Font.DemiBold
115+ }
96116 }
97117 }
98118 Text {
99119 Layout.fillWidth: true
100− color: Theme.fg
120+ color: Theme.fgDim
101121 wrapMode: Text.Wrap
102− font.family: Theme.font
103− font.pixelSize: Theme.fontSize
122+ font.family: Theme.ui
123+ font.pixelSize: 15
104124 text: {
105125 if (win.mode === "polkit") return win.flow ? win.flow.message : ""
106126 if (!win.bt) return ""
107127 var a = win.bt.alias
108128 switch (win.btMethod) {
109− case "RequestConfirmation": return "pair with " + a + "? check that it shows the same code:"
110− case "RequestPinCode": return "enter the PIN for " + a + " (often 0000 or 1234)"
111− case "RequestPasskey": return "enter the 6-digit passkey shown on " + a
129+ case "RequestConfirmation": return "Pair with " + a + "? Check that it shows the same code:"
130+ case "RequestPinCode": return "Enter the PIN for " + a + " (often 0000 or 1234)"
131+ case "RequestPasskey": return "Enter the 6-digit passkey shown on " + a
112132 case "DisplayPasskey":
113− case "DisplayPinCode": return "type this code on " + a + ", then press Enter there:"
114− case "RequestAuthorization": return "allow " + a + " to pair with this computer?"
115− case "AuthorizeService": return "allow " + a + " to use a service?"
133+ case "DisplayPinCode": return "Type this code on " + a + ", then press Enter there:"
134+ case "RequestAuthorization": return "Allow " + a + " to pair with this computer?"
135+ case "AuthorizeService": return "Allow " + a + " to use a service?"
116136 }
117137 return a
118138 }
119139 }
120− Text {
140+ Lb {
121141 Layout.fillWidth: true
122142 visible: text !== ""
143+ px: 10
144+ color: Theme.mute
145+ elide: Text.ElideMiddle
123146 text: win.mode === "polkit" ? (win.flow ? win.flow.actionId : "")
124147 : (win.btMethod === "AuthorizeService" ? win.bt.uuid : "")
125− color: Theme.dim
126− elide: Text.ElideMiddle
127− font.family: Theme.font
128− font.pixelSize: Theme.fontSize - 2
129148 }
130149
131150 // bluetooth passkey / PIN to compare or type on the device
132− Text {
133− Layout.fillWidth: true
151+ Num {
152+ Layout.alignment: Qt.AlignHCenter
134153 visible: win.mode === "bt" && win.btCode !== "" && !win.btInput
135− horizontalAlignment: Text.AlignHCenter
136154 text: win.btCode
137− color: Theme.accent
138− font.family: Theme.font
139− font.pixelSize: Theme.fontSize * 3
140− font.bold: true
155+ font.pixelSize: 64
156+ color: Theme.fill
141157 }
142158
143159 // more than one admin: pick who authenticates
@@ -147,81 +163,100 @@ PanelWindow {
147163 spacing: 4
148164 Repeater {
149165 model: win.mode === "polkit" && win.flow ? win.flow.identities : []
150− delegate: IconBtn {
166+ delegate: Chip {
151167 required property var modelData
152− implicitHeight: 24
153− label: modelData.displayName || String(modelData)
154− active: win.flow && win.flow.selectedIdentity === modelData
168+ text: modelData.displayName || String(modelData)
169+ selected: win.flow && win.flow.selectedIdentity === modelData
155170 onClicked: win.flow.selectedIdentity = modelData
156171 }
157172 }
158173 }
159174
160− Rectangle {
175+ Item {
161176 Layout.fillWidth: true
162177 visible: win.needsInput
163− implicitHeight: 32
164− color: Theme.surface
165− border.width: 1
166− border.color: pass.activeFocus ? Theme.accent : Theme.line
167− Text {
168− id: promptTxt
169− anchors.left: parent.left
170− anchors.leftMargin: 10
171− anchors.verticalCenter: parent.verticalCenter
172− text: win.mode === "bt" ? (win.btMethod === "RequestPinCode" ? "PIN" : "passkey")
173− : (win.flow && win.flow.inputPrompt !== "" ? win.flow.inputPrompt.replace(/:\s*$/, "") : "password")
174− color: Theme.dim
175− font.family: Theme.font
176− font.pixelSize: Theme.fontSize
177− }
178− TextInput {
179− id: pass
180− anchors.fill: parent
181− anchors.leftMargin: promptTxt.implicitWidth + 22
182− anchors.rightMargin: 10
183− verticalAlignment: TextInput.AlignVCenter
184− echoMode: win.mode === "bt" || (win.flow && win.flow.responseVisible) ? TextInput.Normal : TextInput.Password
185− passwordCharacter: "•"
186− maximumLength: win.btMethod === "RequestPasskey" ? 6 : 256
187− validator: RegularExpressionValidator {
188− regularExpression: win.btMethod === "RequestPasskey" ? /[0-9]*/ : /.*/
178+ implicitHeight: 56
179+ Rectangle {
180+ id: field
181+ width: parent.width
182+ height: parent.height
183+ radius: height / 2
184+ color: Theme.s2
185+ border.width: 1
186+ border.color: win.bad ? Theme.err : pass.activeFocus ? Theme.fill : Theme.s3
187+ SequentialAnimation on x {
188+ id: shake
189+ running: false
190+ NumberAnimation { to: -12; duration: 50 }
191+ NumberAnimation { to: 12; duration: 70 }
192+ NumberAnimation { to: -8; duration: 70 }
193+ NumberAnimation { to: 6; duration: 60 }
194+ NumberAnimation { to: 0; duration: 50 }
195+ }
196+ Lb {
197+ id: promptTxt
198+ x: 22
199+ anchors.verticalCenter: parent.verticalCenter
200+ text: win.mode === "bt" ? (win.btMethod === "RequestPinCode" ? "pin" : "passkey")
201+ : (win.flow && win.flow.inputPrompt !== "" ? win.flow.inputPrompt.replace(/:\s*$/, "") : "password")
202+ }
203+ TextInput {
204+ id: pass
205+ anchors.left: promptTxt.right
206+ anchors.leftMargin: 16
207+ anchors.right: parent.right
208+ anchors.rightMargin: 22
209+ anchors.verticalCenter: parent.verticalCenter
210+ echoMode: win.mode === "bt" || (win.flow && win.flow.responseVisible) ? TextInput.Normal : TextInput.Password
211+ passwordCharacter: "•"
212+ maximumLength: win.btMethod === "RequestPasskey" ? 6 : 256
213+ validator: RegularExpressionValidator {
214+ regularExpression: win.btMethod === "RequestPasskey" ? /[0-9]*/ : /.*/
215+ }
216+ enabled: !win.waiting
217+ clip: true
218+ color: Theme.fg
219+ selectionColor: Theme.fill
220+ selectedTextColor: Theme.fillFg
221+ font.family: Theme.mono
222+ font.pixelSize: 16
223+ font.letterSpacing: 3
224+ cursorDelegate: Rectangle {
225+ width: 2
226+ color: Theme.live
227+ visible: pass.activeFocus
228+ Timer { running: pass.activeFocus; interval: 500; repeat: true; onTriggered: parent.opacity = 1 - parent.opacity }
229+ }
230+ Keys.onReturnPressed: win.accept()
231+ Keys.onEnterPressed: win.accept()
232+ Keys.onEscapePressed: win.cancel()
189233 }
190− enabled: !win.waiting
191− clip: true
192− color: Theme.fg
193− selectionColor: Theme.accent
194− selectedTextColor: Theme.bg
195− font.family: Theme.font
196− font.pixelSize: Theme.fontSize
197− Keys.onReturnPressed: win.accept()
198− Keys.onEnterPressed: win.accept()
199− Keys.onEscapePressed: win.cancel()
200234 }
201235 }
202− Text {
236+ Lb {
203237 Layout.fillWidth: true
238+ Layout.leftMargin: 6
204239 visible: win.mode === "polkit" && text !== ""
205− text: win.waiting ? "checking…" : (win.error !== "" ? win.error : (win.flow ? win.flow.supplementaryMessage : ""))
206− color: !win.waiting && (win.error !== "" || (win.flow && win.flow.supplementaryIsError)) ? Theme.err : Theme.dim
240+ px: 10
207241 wrapMode: Text.Wrap
208− font.family: Theme.font
209− font.pixelSize: Theme.fontSize - 1
242+ text: win.waiting ? "checking…" : (win.error !== "" ? win.error : (win.flow ? win.flow.supplementaryMessage : ""))
243+ color: !win.waiting && win.bad ? Theme.err : Theme.grey
210244 }
211245
212246 RowLayout {
213247 Layout.fillWidth: true
214− spacing: 6
248+ Layout.topMargin: 4
249+ spacing: 8
215250 Item { Layout.fillWidth: true }
216251 IconBtn {
217252 visible: !win.btDisplay
218253 label: win.mode === "bt" ? "reject" : "cancel"
219− implicitHeight: 26
254+ implicitHeight: 44
220255 onClicked: win.cancel()
221256 }
222257 IconBtn {
223258 label: win.btDisplay ? "close" : (win.mode === "bt" ? (win.btInput ? "pair" : "allow") : "authenticate")
224− implicitHeight: 26
259+ implicitHeight: 44
225260 active: true
226261 onClicked: win.accept()
227262 }
M .config/quickshell/aurora/CharPicker.qml View
@@ -2,31 +2,28 @@ import QtQuick
22 import QtQuick.Layouts
33 import Quickshell
44 import Quickshell.Wayland
5−import Quickshell.Hyprland
65
76 // Glyph picker (SUPER+grave, `chars` IPC): emoji, nerd font glyphs, unicode symbols, kaomoji, fancy text.
87 // Type to fuzzy search; arrows / Ctrl+HJKL move, Tab section, Ctrl+Left/Right group, Enter copies, Shift+Enter types it
98 // into the focused window, Ctrl+Enter collects into a buffer, Alt+Enter copies the codepoint.
9+// Looks like glyph's charms (github.com/Nebelung-cloud/glyph-rice, GPL-3.0, charms/Charms.qml): a card over the dimmed
10+// screen, search pill, section chips, tiles (the selected one white... here primary), the selection large at the bottom.
1011 PanelWindow {
1112 id: win
1213 property bool opened: false
1314 signal closeRequested()
1415
15− visible: opened
16− implicitWidth: 780
17− implicitHeight: 590
18− exclusiveZone: 0
16+ property real t: opened ? 1 : 0
17+ Behavior on t { NumberAnimation { duration: Theme.t; easing.type: Easing.BezierSpline; easing.bezierCurve: Theme.glide } }
18+
19+ visible: t > 0
20+ anchors { top: true; bottom: true; left: true; right: true }
21+ exclusionMode: ExclusionMode.Ignore
1922 color: "transparent"
2023 WlrLayershell.layer: WlrLayer.Overlay
2124 WlrLayershell.namespace: "aurora-chars"
2225 WlrLayershell.keyboardFocus: opened ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
2326
24− HyprlandFocusGrab {
25− windows: [win]
26− active: win.opened
27− onCleared: win.closeRequested()
28− }
29−
3027 readonly property var sections: [
3128 { id: "all", label: "all", icon: Theme.iGrid, sets: ["emoji", "nerd", "sym"] },
3229 { id: "recent", label: "recent", icon: Theme.iHistory, sets: ["emoji", "nerd", "sym"] },
@@ -147,56 +144,76 @@ PanelWindow {
147144
148145 Rectangle {
149146 anchors.fill: parent
150− color: Theme.bgA
151− border.width: 2
152− border.color: Theme.accent
147+ color: Theme.scrim
148+ opacity: win.t
149+ MouseArea { anchors.fill: parent; onClicked: win.closeRequested() }
150+ }
151+
152+ Rectangle {
153+ id: card
154+ anchors.horizontalCenter: parent.horizontalCenter
155+ y: Math.round((parent.height - height) / 2) - 30 + 24 * (1 - win.t)
156+ width: Math.min(parent.width - 80, 960)
157+ height: Math.min(parent.height - 160, 740)
158+ opacity: win.t
159+ radius: 44
160+ color: Qt.rgba(Theme.s1.r, Theme.s1.g, Theme.s1.b, Theme.panelAlpha)
161+ border.width: 1
162+ border.color: Theme.s2
163+ MouseArea { anchors.fill: parent }
153164
154165 ColumnLayout {
155166 anchors.fill: parent
156− anchors.margins: 12
157− spacing: 8
167+ anchors.margins: 30
168+ spacing: 14
158169
159170 // ---- search + tone + close ----
160171 RowLayout {
161172 Layout.fillWidth: true
162− spacing: 6
173+ spacing: 10
163174 Rectangle {
164175 Layout.fillWidth: true
165− implicitHeight: 30
166− color: Theme.surface
167− border.width: 1
168− border.color: filter.activeFocus ? Theme.accent : Theme.line
169− Text {
170− anchors.left: parent.left
171− anchors.leftMargin: 10
176+ implicitHeight: 60
177+ radius: 30
178+ color: Theme.s2
179+ Icon {
180+ id: lens
181+ x: 22
172182 anchors.verticalCenter: parent.verticalCenter
173183 text: Theme.iSearch
174− color: Theme.dim
175− font.family: Theme.font
176− font.pixelSize: Theme.fontSize
184+ color: Theme.grey
185+ font.pixelSize: 20
177186 }
178187 Text {
179− anchors.left: parent.left
180− anchors.leftMargin: 32
188+ anchors.left: filter.left
189+ anchors.right: count.left
181190 anchors.verticalCenter: parent.verticalCenter
182191 visible: filter.text === ""
183− text: win.section.id === "fancy" ? "type some text" : "search " + win.section.label + " (name, keyword, u+f0001, or paste a glyph)"
184− color: Theme.dim
185− font.family: Theme.font
186− font.pixelSize: Theme.fontSize
192+ elide: Text.ElideRight
193+ text: win.section.id === "fancy" ? "Type some text" : "Search " + win.section.label + " (name, keyword, u+f0001, or paste a glyph)"
194+ color: Theme.mute
195+ font.family: Theme.ui
196+ font.pixelSize: 18
187197 }
188198 TextInput {
189199 id: filter
190− anchors.fill: parent
191− anchors.leftMargin: 32
192− anchors.rightMargin: 10
193− verticalAlignment: TextInput.AlignVCenter
200+ anchors.left: lens.right
201+ anchors.leftMargin: 14
202+ anchors.right: count.left
203+ anchors.rightMargin: 12
204+ anchors.verticalCenter: parent.verticalCenter
194205 color: Theme.fg
195− selectionColor: Theme.accent
196− selectedTextColor: Theme.bg
197− font.family: Theme.font
198− font.pixelSize: Theme.fontSize
206+ selectionColor: Theme.fill
207+ selectedTextColor: Theme.fillFg
208+ font.family: Theme.ui
209+ font.pixelSize: 18
199210 clip: true
211+ cursorDelegate: Rectangle {
212+ width: 2
213+ color: Theme.live
214+ visible: filter.activeFocus
215+ Timer { running: filter.activeFocus; interval: 500; repeat: true; onTriggered: parent.opacity = 1 - parent.opacity }
216+ }
200217 onTextChanged: {
201218 // recent is the landing page: typing there searches everything (used ones rank first anyway)
202219 if (text !== "" && win.section.id === "recent") win.sec = 0
@@ -235,32 +252,32 @@ PanelWindow {
235252 event.accepted = true
236253 }
237254 }
238− }
239− Text {
240− text: win.results.length
241− color: Theme.dim
242− font.family: Theme.font
243− font.pixelSize: Theme.fontSize - 1
255+ Lb {
256+ id: count
257+ anchors.right: parent.right
258+ anchors.rightMargin: 22
259+ anchors.verticalCenter: parent.verticalCenter
260+ text: win.results.length
261+ }
244262 }
245263 // skin tone: the raised hand in each tone, click to choose (persisted)
246264 Row {
247265 visible: Chars.loaded && (win.section.id === "emoji" || win.section.id === "all" || win.section.id === "recent")
248− spacing: 0
266+ spacing: 2
249267 Repeater {
250268 model: 6
251269 delegate: Rectangle {
252270 required property int index
253271 readonly property var hand: Chars.byChar["✋"]
254− width: 26
255− height: 30
256− color: Settings.charTone === index ? Theme.surfaceHi : (tm.containsMouse ? Theme.surface : "transparent")
257− border.width: Settings.charTone === index ? 1 : 0
258− border.color: Theme.accent
272+ width: 40
273+ height: 40
274+ radius: 20
275+ color: Settings.charTone === index ? Theme.fill : (tm.containsMouse ? Theme.s3 : "transparent")
259276 Text {
260277 anchors.centerIn: parent
261278 text: parent.hand ? Chars.glyph(parent.hand, parent.index) : ""
262279 font.family: "Noto Color Emoji"
263− font.pixelSize: 15
280+ font.pixelSize: 17
264281 }
265282 MouseArea {
266283 id: tm
@@ -272,56 +289,21 @@ PanelWindow {
272289 }
273290 }
274291 }
275− IconBtn { icon: Theme.iClose; onClicked: win.closeRequested() }
292+ IconBtn { implicitHeight: 48; icon: Theme.iClose; onClicked: win.closeRequested() }
276293 }
277294
278295 // ---- sections ----
279− RowLayout {
296+ Row {
280297 Layout.fillWidth: true
281298 spacing: 2
282299 Repeater {
283300 model: win.sections
284− delegate: Rectangle {
285− id: tab
301+ delegate: Chip {
286302 required property var modelData
287303 required property int index
288− readonly property bool active: win.sec === index
289− Layout.fillWidth: true
290− implicitHeight: 28
291− color: active ? Theme.surfaceHi : (sm.containsMouse ? Theme.surface : "transparent")
292− Row {
293− anchors.centerIn: parent
294− spacing: 6
295− Text {
296− anchors.verticalCenter: parent.verticalCenter
297− text: tab.modelData.icon
298− color: tab.active ? Theme.accent : Theme.dim
299− font.family: Theme.font
300− font.pixelSize: Theme.fontSize + 2
301− }
302− Text {
303− anchors.verticalCenter: parent.verticalCenter
304− text: tab.modelData.label.toUpperCase()
305− color: tab.active ? Theme.fg : Theme.dim
306− font.family: Theme.font
307− font.pixelSize: Theme.fontSize - 2
308− font.letterSpacing: 1.2
309− font.bold: tab.active
310− }
311− }
312− Rectangle { // waybar-style underline
313− anchors.bottom: parent.bottom
314− width: parent.width
315− height: tab.active ? 2 : 1
316− color: tab.active ? Theme.accent : Theme.line
317− }
318− MouseArea {
319− id: sm
320− anchors.fill: parent
321− hoverEnabled: true
322− cursorShape: Qt.PointingHandCursor
323− onClicked: { win.sec = tab.index; filter.forceActiveFocus() }
324− }
304+ text: modelData.label
305+ selected: win.sec === index
306+ onClicked: { win.sec = index; filter.forceActiveFocus() }
325307 }
326308 }
327309 }
@@ -330,7 +312,7 @@ PanelWindow {
330312 ListView {
331313 id: chips
332314 Layout.fillWidth: true
333− implicitHeight: 22
315+ implicitHeight: 32
334316 visible: win.groups.length > 0
335317 orientation: ListView.Horizontal
336318 spacing: 4
@@ -339,31 +321,15 @@ PanelWindow {
339321 model: win.groups.length ? ["all"].concat(win.groups) : []
340322 currentIndex: win.grp + 1
341323 onCurrentIndexChanged: positionViewAtIndex(currentIndex, ListView.Contain)
342− delegate: Rectangle {
343− id: chip
324+ delegate: Chip {
344325 required property var modelData
345326 required property int index
346− readonly property bool active: win.grp === index - 1
347− width: ct.implicitWidth + 16
348− height: 22
349− color: active ? Theme.accent : (cm.containsMouse ? Theme.surfaceHi : Theme.surface)
350− border.width: 1
351− border.color: active ? Theme.accent : Theme.line
352− Text {
353− id: ct
354− anchors.centerIn: parent
355− text: chip.modelData.toLowerCase()
356− color: chip.active ? Theme.bg : Theme.fg
357− font.family: Theme.font
358− font.pixelSize: Theme.fontSize - 2
359− }
360− MouseArea {
361− id: cm
362− anchors.fill: parent
363− hoverEnabled: true
364− cursorShape: Qt.PointingHandCursor
365− onClicked: { win.grp = chip.index - 1; filter.forceActiveFocus() }
366− }
327+ height: 32
328+ px: 10
329+ filled: true
330+ text: modelData
331+ selected: win.grp === index - 1
332+ onClicked: { win.grp = index - 1; filter.forceActiveFocus() }
367333 }
368334 }
369335
@@ -376,9 +342,9 @@ PanelWindow {
376342 id: grid
377343 anchors.fill: parent
378344 visible: !win.listMode
379− readonly property int cols: Math.max(1, Math.floor(width / 50))
345+ readonly property int cols: Math.max(1, Math.floor(width / 58))
380346 cellWidth: Math.floor(width / cols)
381− cellHeight: 50
347+ cellHeight: 58
382348 clip: true
383349 model: win.listMode ? 0 : win.results.length
384350 boundsBehavior: Flickable.StopAtBounds
@@ -390,15 +356,14 @@ PanelWindow {
390356 required property int index
391357 readonly property var it: win.results[index]
392358 readonly property bool selected: grid.currentIndex === index
393− width: grid.cellWidth - 2
394− height: grid.cellHeight - 2
395− color: selected ? Theme.surfaceHi : (gm.containsMouse ? Theme.surface : "transparent")
396− border.width: selected ? 1 : 0
397− border.color: Theme.accent
359+ width: grid.cellWidth - 4
360+ height: grid.cellHeight - 4
361+ radius: 18
362+ color: selected ? Theme.fill : (gm.containsMouse ? Theme.s2 : "transparent")
398363 Text {
399364 anchors.centerIn: parent
400365 text: cell.it ? Chars.glyph(cell.it, win.tone) : ""
401− color: Theme.fg
366+ color: cell.selected ? Theme.fillFg : Theme.fg
402367 font.family: win.fontOf(cell.it)
403368 font.pixelSize: 26
404369 }
@@ -432,38 +397,30 @@ PanelWindow {
432397 readonly property var it: win.results[index]
433398 readonly property bool selected: list.currentIndex === index
434399 width: list.width
435− height: 34
436− color: selected ? Theme.surfaceHi : (lm.containsMouse ? Theme.surface : "transparent")
437− border.width: selected ? 1 : 0
438− border.color: Theme.accent
439− Rectangle {
440− visible: row.selected
441− width: 2
442− height: parent.height
443− color: Theme.accent
444− }
400+ height: 52
401+ radius: 26
402+ color: selected ? Theme.fill : (lm.containsMouse ? Theme.s2 : "transparent")
445403 Text {
446404 anchors.left: parent.left
447− anchors.leftMargin: 12
405+ anchors.leftMargin: 22
448406 anchors.right: tag.left
449407 anchors.rightMargin: 12
450408 anchors.verticalCenter: parent.verticalCenter
451409 text: row.it ? row.it.c : ""
452410 elide: Text.ElideRight
453411 maximumLineCount: 1
454− color: Theme.fg
455− font.family: Theme.font
456− font.pixelSize: Theme.fontSize + 4
412+ color: row.selected ? Theme.fillFg : Theme.fg
413+ font.family: Theme.ui
414+ font.pixelSize: 18
457415 }
458− Text {
416+ Lb {
459417 id: tag
460418 anchors.right: parent.right
461− anchors.rightMargin: 12
419+ anchors.rightMargin: 22
462420 anchors.verticalCenter: parent.verticalCenter
421+ px: 10
463422 text: row.it ? (win.section.id === "fancy" ? row.it.n : row.it.k) : ""
464− color: row.selected ? Theme.accent : Theme.dim
465− font.family: Theme.font
466− font.pixelSize: Theme.fontSize - 1
423+ color: row.selected ? Theme.fillFg : Theme.grey
467424 }
468425 MouseArea {
469426 id: lm
@@ -480,98 +437,90 @@ PanelWindow {
480437 }
481438 }
482439
483− Text {
440+ Lb {
484441 anchors.centerIn: parent
485442 visible: win.results.length === 0
486− text: !Chars.loaded && win.section.id !== "fancy" ? "loading glyphs..."
443+ color: Theme.mute
444+ text: !Chars.loaded && win.section.id !== "fancy" ? "loading glyphs…"
487445 : (win.section.id === "recent" && filter.text === "" ? "nothing used yet" : "no matches")
488− color: Theme.dim
489− font.family: Theme.font
490− font.pixelSize: Theme.fontSize
491446 }
492447 }
493448
494− // ---- preview of the selected / hovered glyph ----
449+ // ---- the selected / hovered glyph, large ----
495450 Rectangle {
496451 Layout.fillWidth: true
497− implicitHeight: 62
498− color: Theme.surface
499− border.width: 1
500− border.color: Theme.line
452+ implicitHeight: 92
453+ radius: 30
454+ color: Theme.s2
501455
502− Rectangle { // big glyph
456+ Rectangle {
503457 id: big
504458 visible: !win.listMode
505− anchors.left: parent.left
506− anchors.top: parent.top
507− anchors.bottom: parent.bottom
508− width: visible ? height : 0
509− color: "transparent"
510− border.width: 1
511− border.color: Theme.line
459+ x: 12
460+ anchors.verticalCenter: parent.verticalCenter
461+ width: visible ? 68 : 0
462+ height: 68
463+ radius: 34
464+ color: Theme.s3
512465 Text {
513466 anchors.centerIn: parent
514467 text: win.current ? Chars.glyph(win.current, win.tone) : ""
515− color: Theme.accent
468+ color: Theme.fill
516469 font.family: win.fontOf(win.current)
517− font.pixelSize: 38
470+ font.pixelSize: 36
518471 }
519472 }
520473 Column {
521474 anchors.left: big.right
522− anchors.leftMargin: 12
475+ anchors.leftMargin: 16
523476 anchors.right: buf.left
524477 anchors.rightMargin: 12
525478 anchors.verticalCenter: parent.verticalCenter
526− spacing: 3
479+ spacing: 4
527480 Text {
528481 width: parent.width
529482 text: win.current ? win.current.n : ""
530483 elide: Text.ElideRight
531484 color: Theme.fg
532− font.family: Theme.font
533− font.pixelSize: Theme.fontSize + 1
534− font.bold: true
485+ font.family: Theme.ui
486+ font.pixelSize: 16
487+ font.weight: Font.DemiBold
535488 }
536− Text {
489+ Lb {
537490 width: parent.width
538491 visible: win.current !== null && win.section.id !== "fancy"
492+ px: 10
493+ color: Theme.fill
539494 text: {
540495 var c = win.current
541496 if (!c || c.s === undefined || !Chars.sets[c.s]) return ""
542497 var st = Chars.sets[c.s]
543− return (st.id === "kao" ? "" : Chars.hex(Chars.glyph(c, win.tone)) + " ")
544− + st.label.toLowerCase() + " > " + st.groups[c.g].toLowerCase()
498+ return (st.id === "kao" ? "" : Chars.hex(Chars.glyph(c, win.tone)) + " · ")
499+ + st.label + " · " + st.groups[c.g]
545500 }
546501 elide: Text.ElideRight
547− color: Theme.accent
548− font.family: Theme.font
549− font.pixelSize: Theme.fontSize - 1
550502 }
551− Text {
503+ Lb {
552504 width: parent.width
553505 visible: text !== ""
506+ px: 10
554507 text: win.current && win.current.k && win.section.id !== "kao" ? win.current.k : ""
555508 elide: Text.ElideRight
556− color: Theme.dim
557− font.family: Theme.font
558− font.pixelSize: Theme.fontSize - 2
559509 }
560510 }
561511 Row { // collected with ctrl+enter / right click
562512 id: buf
563513 anchors.right: parent.right
564− anchors.rightMargin: 12
514+ anchors.rightMargin: 22
565515 anchors.verticalCenter: parent.verticalCenter
566− spacing: 8
516+ spacing: 10
567517 visible: win.buffer !== ""
568518 width: visible ? implicitWidth : 0
569− Text {
519+ Icon {
570520 anchors.verticalCenter: parent.verticalCenter
571521 text: Theme.iBuffer
572− color: Theme.accent
573− font.family: Theme.font
574− font.pixelSize: Theme.fontSize + 2
522+ color: Theme.live
523+ font.pixelSize: 18
575524 }
576525 Text {
577526 anchors.verticalCenter: parent.verticalCenter
@@ -580,20 +529,22 @@ PanelWindow {
580529 elide: Text.ElideLeft
581530 color: Theme.fg
582531 font.family: Theme.font
583− font.pixelSize: Theme.fontSize + 4
532+ font.pixelSize: 20
584533 }
585534 }
586535 }
536+ }
537+ }
587538
588− Text {
589− Layout.fillWidth: true
590− text: "enter copy " + (Chars.hasWtype ? "shift+enter type " : "") + "ctrl+enter collect alt+enter code "
591− + "ctrl+hjkl move tab section ctrl+arrows group esc close"
592− elide: Text.ElideRight
593− color: Theme.dim
594− font.family: Theme.font
595− font.pixelSize: Theme.fontSize - 2
596− }
539+ Row {
540+ anchors.horizontalCenter: parent.horizontalCenter
541+ anchors.top: card.bottom
542+ anchors.topMargin: 26
543+ opacity: win.t
544+ spacing: 30
545+ Repeater {
546+ model: ["↵ copy"].concat(Chars.hasWtype ? ["shift ↵ type"] : []).concat(["ctrl ↵ collect", "alt ↵ code", "ctrl hjkl move", "tab section", "ctrl ←→ group", "esc close"])
547+ Lb { required property string modelData; text: modelData }
597548 }
598549 }
599550 }
M .config/quickshell/aurora/ClipWindow.qml View
@@ -1,33 +1,34 @@
11 import QtQuick
22 import QtQuick.Layouts
3+import QtQuick.Effects
34 import Quickshell
45 import Quickshell.Wayland
5−import Quickshell.Hyprland
66
7−// Clipboard history: type to filter, Up/Down, Enter copies + closes, Delete removes, Esc closes.
7+// Clipboard history (cliphist): a card over the dimmed screen. Search pill with the live caret, filter chips
8+// (Tab cycles: all / text / links / images), the history as rows (type in a circle: text, link, code, the colour
9+// itself, the image as dots; a Doto number) and the selected entry in full on the right.
10+// Enter copies + closes, Delete removes, Esc or a click outside closes.
11+// After glyph-rice (github.com/Nebelung-cloud/glyph-rice, GPL-3.0), clip/Clip.qml.
812 PanelWindow {
913 id: win
1014 property bool opened: false
1115 signal closeRequested()
1216
13− visible: opened
14− implicitWidth: 620
15− implicitHeight: 560
16− exclusiveZone: 0
17+ property real t: opened ? 1 : 0
18+ Behavior on t { NumberAnimation { duration: Theme.t; easing.type: Easing.BezierSpline; easing.bezierCurve: Theme.glide } }
19+
20+ visible: t > 0
21+ anchors { top: true; bottom: true; left: true; right: true }
22+ exclusionMode: ExclusionMode.Ignore
1723 color: "transparent"
1824 WlrLayershell.layer: WlrLayer.Overlay
1925 WlrLayershell.namespace: "aurora-clipboard"
2026 WlrLayershell.keyboardFocus: opened ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
2127
22− HyprlandFocusGrab {
23− windows: [win]
24− active: win.opened
25− onCleared: win.closeRequested()
26− }
27−
2828 onOpenedChanged: {
2929 if (!opened) return
3030 filter.text = ""
31+ kind = 0
3132 armed = false
3233 Clip.refresh()
3334 list.currentIndex = 0
@@ -37,186 +38,323 @@ PanelWindow {
3738 property bool armed: false
3839 Timer { id: disarm; interval: 3000; onTriggered: win.armed = false }
3940
41+ readonly property var kinds: ["all", "text", "links", "images"]
42+ property int kind: 0
43+ function typeOf(e) {
44+ if (e.image) return "image"
45+ var t = e.text.trim()
46+ if (/^(https?|ftp|file):\/\/\S+$/i.test(t) || /^www\.\S+$/i.test(t)) return "link"
47+ if (/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(t) || /^rgba?\(/i.test(t)) return "colour"
48+ if (/[{};]|=>|^\s*(sudo|git|cd|ls|cat|echo|hyprctl|qs|pacman|paru|systemctl|python3?)\s/.test(t)) return "code"
49+ return "text"
50+ }
4051 readonly property var shown: {
41− var q = filter.text.toLowerCase(), out = [], l = Clip.entries
42− for (var i = 0; i < l.length; i++)
52+ var q = filter.text.toLowerCase(), out = [], l = Clip.entries, k = kinds[kind]
53+ for (var i = 0; i < l.length; i++) {
54+ var ty = typeOf(l[i])
55+ if (k === "text" && (ty === "image" || ty === "link")) continue
56+ if (k === "links" && ty !== "link") continue
57+ if (k === "images" && ty !== "image") continue
4358 if (q === "" || l[i].text.toLowerCase().indexOf(q) >= 0) out.push(l[i])
59+ }
4460 return out
4561 }
62+ readonly property var cur: list.currentIndex >= 0 && list.currentIndex < shown.length ? shown[list.currentIndex] : null
4663 function pick(e) {
4764 if (!e) return
4865 Clip.copy(e.id)
4966 closeRequested()
5067 }
68+ function icon(ty) { return ty === "link" ? Theme.iLink : ty === "code" ? Theme.iCode : ty === "image" ? Theme.iImage : Theme.iText }
5169
5270 Rectangle {
5371 anchors.fill: parent
54− color: Theme.bgA
55− border.width: 2
56− border.color: Theme.accent
72+ color: Theme.scrim
73+ opacity: win.t
74+ MouseArea { anchors.fill: parent; onClicked: win.closeRequested() }
75+ }
5776
58− ColumnLayout {
59− anchors.fill: parent
60− anchors.margins: 12
61− spacing: 10
77+ Rectangle {
78+ id: card
79+ anchors.horizontalCenter: parent.horizontalCenter
80+ y: Math.round((parent.height - height) / 2) - 30 + 24 * (1 - win.t)
81+ width: Math.min(parent.width - 80, 1160)
82+ height: Math.min(parent.height - 160, 780)
83+ opacity: win.t
84+ radius: 44
85+ color: Qt.rgba(Theme.s1.r, Theme.s1.g, Theme.s1.b, Theme.panelAlpha)
86+ border.width: 1
87+ border.color: Theme.s2
88+ MouseArea { anchors.fill: parent }
6289
63− RowLayout {
64− Layout.fillWidth: true
65− spacing: 6
66− Rectangle {
67− Layout.fillWidth: true
68− implicitHeight: 30
69− color: Theme.surface
70− border.width: 1
71− border.color: filter.activeFocus ? Theme.accent : Theme.line
72− Text {
73− anchors.left: parent.left
74− anchors.leftMargin: 10
75− anchors.verticalCenter: parent.verticalCenter
76− text: Theme.iClipboard
77− color: Theme.dim
78− font.family: Theme.font
79− font.pixelSize: Theme.fontSize
80− }
81− TextInput {
82− id: filter
83− anchors.fill: parent
84− anchors.leftMargin: 32
85− anchors.rightMargin: 10
86− verticalAlignment: TextInput.AlignVCenter
87− color: Theme.fg
88− selectionColor: Theme.accent
89− selectedTextColor: Theme.bg
90− font.family: Theme.font
91− font.pixelSize: Theme.fontSize
92− clip: true
93− onTextChanged: list.currentIndex = 0
94− Keys.onEscapePressed: win.closeRequested()
95− Keys.onReturnPressed: win.pick(win.shown[list.currentIndex])
96− Keys.onEnterPressed: win.pick(win.shown[list.currentIndex])
97− Keys.onUpPressed: list.decrementCurrentIndex()
98− Keys.onDownPressed: list.incrementCurrentIndex()
99− Keys.onPressed: event => {
100− if (event.key === Qt.Key_Delete && list.currentIndex >= 0 && win.shown.length) {
101− Clip.remove(win.shown[list.currentIndex].id)
102− event.accepted = true
103− } else if (event.key === Qt.Key_PageDown) {
104− list.currentIndex = Math.min(win.shown.length - 1, list.currentIndex + 10); event.accepted = true
105− } else if (event.key === Qt.Key_PageUp) {
106− list.currentIndex = Math.max(0, list.currentIndex - 10); event.accepted = true
107− }
108− }
109− }
90+ // ---- search ----
91+ Rectangle {
92+ id: search
93+ x: 34
94+ y: 30
95+ width: parent.width - 68
96+ height: 66
97+ radius: 33
98+ color: Theme.s2
99+ Icon { id: lens; x: 24; anchors.verticalCenter: parent.verticalCenter; text: Theme.iSearch; color: Theme.grey; font.pixelSize: 22 }
100+ TextInput {
101+ id: filter
102+ anchors.left: lens.right
103+ anchors.leftMargin: 16
104+ anchors.right: chips.left
105+ anchors.rightMargin: 12
106+ anchors.verticalCenter: parent.verticalCenter
107+ font.family: Theme.ui
108+ font.pixelSize: 22
109+ color: Theme.fg
110+ selectionColor: Theme.fill
111+ selectedTextColor: Theme.fillFg
112+ clip: true
113+ cursorDelegate: Rectangle {
114+ width: 2
115+ color: Theme.live
116+ visible: filter.activeFocus
117+ Timer { running: filter.activeFocus; interval: 500; repeat: true; onTriggered: parent.opacity = 1 - parent.opacity }
110118 }
111119 Text {
112− text: win.shown.length
113− color: Theme.dim
114− font.family: Theme.font
115− font.pixelSize: Theme.fontSize - 1
120+ visible: !filter.text
121+ text: "Search clipboard"
122+ font: filter.font
123+ color: Theme.mute
116124 }
117− IconBtn {
118− icon: Theme.iTrash
119− label: win.armed ? "sure?" : "wipe"
120− active: win.armed
121− onClicked: {
122− if (!win.armed) { win.armed = true; disarm.restart(); return }
123− win.armed = false
124− Clip.wipe()
125+ onTextChanged: list.currentIndex = 0
126+ Keys.onEscapePressed: win.closeRequested()
127+ Keys.onReturnPressed: win.pick(win.cur)
128+ Keys.onEnterPressed: win.pick(win.cur)
129+ Keys.onUpPressed: list.decrementCurrentIndex()
130+ Keys.onDownPressed: list.incrementCurrentIndex()
131+ Keys.onTabPressed: { win.kind = (win.kind + 1) % win.kinds.length; list.currentIndex = 0 }
132+ Keys.onBacktabPressed: { win.kind = (win.kind + win.kinds.length - 1) % win.kinds.length; list.currentIndex = 0 }
133+ Keys.onPressed: event => {
134+ if (event.key === Qt.Key_Delete && win.cur) {
135+ Clip.remove(win.cur.id)
136+ event.accepted = true
137+ } else if (event.key === Qt.Key_PageDown) {
138+ list.currentIndex = Math.min(win.shown.length - 1, list.currentIndex + 10); event.accepted = true
139+ } else if (event.key === Qt.Key_PageUp) {
140+ list.currentIndex = Math.max(0, list.currentIndex - 10); event.accepted = true
125141 }
126142 }
127− IconBtn { icon: Theme.iClose; onClicked: win.closeRequested() }
128143 }
129−
130− ListView {
131− id: list
132− Layout.fillWidth: true
133− Layout.fillHeight: true
134− clip: true
135− model: win.shown
136− spacing: 2
137− boundsBehavior: Flickable.StopAtBounds
138− highlightMoveDuration: 0
139−
140− delegate: Rectangle {
141− id: row
142− required property var modelData
143− required property int index
144− readonly property bool selected: list.currentIndex === index
145− readonly property bool hasImg: modelData.image && Clip.ready[modelData.id] === true
146− width: list.width
147− height: hasImg ? 96 : 28
148− color: selected ? Theme.surfaceHi : (rm.containsMouse ? Theme.surface : "transparent")
149− border.width: selected ? 1 : 0
150− border.color: Theme.accent
151−
152− Rectangle { // selection marker, like the bar's active workspace
153− visible: row.selected
154− width: 2
155− height: parent.height
156− color: Theme.accent
144+ Row {
145+ id: chips
146+ anchors.right: parent.right
147+ anchors.rightMargin: 12
148+ anchors.verticalCenter: parent.verticalCenter
149+ spacing: 6
150+ Repeater {
151+ model: win.kinds
152+ Chip {
153+ required property string modelData
154+ required property int index
155+ text: modelData
156+ selected: win.kind === index
157+ onClicked: { win.kind = index; list.currentIndex = 0; filter.forceActiveFocus() }
157158 }
158− Row {
159− anchors.fill: parent
160− anchors.leftMargin: 10
161− anchors.rightMargin: 34
162− spacing: 10
163− Image {
164− visible: row.hasImg
165− anchors.verticalCenter: parent.verticalCenter
166− width: visible ? 150 : 0
167− height: 84
168− fillMode: Image.PreserveAspectFit
169− asynchronous: true
170− sourceSize.width: 300
171− sourceSize.height: 168
172− source: row.hasImg ? "file://" + Clip.imageOf(row.modelData) : ""
173− }
174− Text {
175− anchors.verticalCenter: parent.verticalCenter
176− width: parent.width - (row.hasImg ? 160 : 0)
177− text: (row.modelData.image ? Theme.iImage + " " : "") + row.modelData.text
178− elide: Text.ElideRight
179− maximumLineCount: 1
180− color: row.selected ? Theme.fg : (row.modelData.image ? Theme.accent : Theme.fg)
181− font.family: Theme.font
182− font.pixelSize: Theme.fontSize
183− }
159+ }
160+ }
161+ }
162+
163+ // ---- the history ----
164+ Row {
165+ id: head
166+ x: 40
167+ anchors.top: search.bottom
168+ anchors.topMargin: 22
169+ spacing: 14
170+ Num { anchors.baseline: hl.baseline; text: win.shown.length; font.pixelSize: 34 }
171+ Lb { id: hl; text: win.shown.length === 1 ? "entry" : "entries" }
172+ }
173+ Row {
174+ anchors.right: list.right
175+ anchors.verticalCenter: head.verticalCenter
176+ spacing: 8
177+ IconBtn {
178+ visible: Clip.entries.length > 0
179+ icon: Theme.iTrash
180+ label: win.armed ? "sure?" : "wipe all"
181+ active: win.armed
182+ onClicked: {
183+ if (!win.armed) { win.armed = true; disarm.restart(); return }
184+ win.armed = false
185+ Clip.wipe()
186+ }
187+ }
188+ }
189+
190+ ListView {
191+ id: list
192+ x: 34
193+ anchors.top: head.bottom
194+ anchors.topMargin: 18
195+ anchors.bottom: parent.bottom
196+ anchors.bottomMargin: 34
197+ width: Math.round((parent.width - 68) * 0.48)
198+ clip: true
199+ model: win.shown
200+ spacing: 4
201+ boundsBehavior: Flickable.StopAtBounds
202+ highlightMoveDuration: 0
203+
204+ delegate: Rectangle {
205+ id: row
206+ required property var modelData
207+ required property int index
208+ readonly property bool selected: list.currentIndex === index
209+ readonly property string ty: win.typeOf(modelData)
210+ readonly property bool hasImg: modelData.image && Clip.ready[modelData.id] === true
211+ width: list.width
212+ height: 66
213+ radius: 33
214+ color: selected ? Theme.fill : rm.containsMouse ? Theme.s2 : "transparent"
215+
216+ MouseArea {
217+ id: rm
218+ anchors.fill: parent
219+ hoverEnabled: true
220+ cursorShape: Qt.PointingHandCursor
221+ onEntered: list.currentIndex = row.index
222+ onClicked: win.pick(row.modelData)
223+ }
224+ Rectangle {
225+ id: ic
226+ x: 8
227+ anchors.verticalCenter: parent.verticalCenter
228+ width: 50
229+ height: 50
230+ radius: 25
231+ color: row.ty === "colour" ? row.modelData.text.trim() : row.selected ? Theme.fillFg : Theme.s2
232+ Icon {
233+ anchors.centerIn: parent
234+ visible: row.ty !== "colour" && !thumb.visible
235+ text: win.icon(row.ty)
236+ font.pixelSize: 20
237+ color: row.selected ? Theme.fill : Theme.fg
184238 }
185− MouseArea {
186− id: rm
239+ Image {
240+ id: thumb
187241 anchors.fill: parent
188− hoverEnabled: true
189− cursorShape: Qt.PointingHandCursor
190− onClicked: win.pick(row.modelData)
242+ visible: row.hasImg && status === Image.Ready
243+ source: row.hasImg ? "file://" + Clip.imageOf(row.modelData) : ""
244+ fillMode: Image.PreserveAspectCrop
245+ sourceSize: Qt.size(100, 100)
246+ asynchronous: true
247+ layer.enabled: true
248+ layer.effect: DotMask { step: 3; circle: true }
191249 }
192− Text { // delete
193− anchors.right: parent.right
194− anchors.rightMargin: 10
195− anchors.verticalCenter: parent.verticalCenter
196− visible: row.selected
197− text: Theme.iClose
198− color: dm.containsMouse ? Theme.err : Theme.dim
199− font.family: Theme.font
200− font.pixelSize: Theme.fontSize
201− MouseArea {
202− id: dm
203− anchors.fill: parent
204− anchors.margins: -4
205− hoverEnabled: true
206− cursorShape: Qt.PointingHandCursor
207− onClicked: Clip.remove(row.modelData.id)
208− }
250+ }
251+ Column {
252+ anchors.left: ic.right
253+ anchors.leftMargin: 14
254+ anchors.right: num.left
255+ anchors.rightMargin: 12
256+ anchors.verticalCenter: parent.verticalCenter
257+ Text {
258+ width: parent.width
259+ elide: Text.ElideRight
260+ maximumLineCount: 1
261+ text: row.modelData.text.replace(/\s+/g, " ")
262+ font.family: Theme.ui
263+ font.pixelSize: 15
264+ color: row.selected ? Theme.fillFg : Theme.fg
209265 }
266+ Lb { text: row.ty; px: 10; color: row.selected ? Theme.fillFg : Theme.grey }
210267 }
268+ Num {
269+ id: num
270+ anchors.right: parent.right
271+ anchors.rightMargin: 22
272+ anchors.verticalCenter: parent.verticalCenter
273+ text: ("0" + (row.index + 1)).slice(-2)
274+ font.pixelSize: 28
275+ color: row.selected ? Theme.fillFg : Theme.mute
276+ }
277+ }
278+ Lb {
279+ visible: win.shown.length === 0
280+ anchors.centerIn: parent
281+ color: Theme.mute
282+ text: Clip.available ? (Clip.entries.length ? "no match" : "clipboard is empty") : "cliphist is not installed"
211283 }
284+ }
212285
286+ // ---- the selected entry in full ----
287+ Rectangle {
288+ id: preview
289+ anchors.left: list.right
290+ anchors.leftMargin: 24
291+ anchors.right: parent.right
292+ anchors.rightMargin: 34
293+ anchors.top: search.bottom
294+ anchors.topMargin: 30
295+ anchors.bottom: parent.bottom
296+ anchors.bottomMargin: 34
297+ radius: 30
298+ color: Theme.s2
299+ readonly property var e: win.cur
300+ readonly property string ty: e ? win.typeOf(e) : ""
301+
302+ Lb {
303+ id: pl
304+ x: 30
305+ y: 28
306+ text: preview.e ? preview.ty + " · " + (list.currentIndex + 1) + " of " + win.shown.length : ""
307+ }
213308 Text {
214− Layout.fillWidth: true
215− text: Clip.available ? "enter copy del remove esc close" : "cliphist not installed"
216− color: Clip.available ? Theme.dim : Theme.err
217− font.family: Theme.font
218− font.pixelSize: Theme.fontSize - 2
309+ visible: preview.e !== null && preview.ty !== "image"
310+ anchors.left: parent.left
311+ anchors.right: parent.right
312+ anchors.top: pl.bottom
313+ anchors.bottom: parent.bottom
314+ anchors.margins: 30
315+ wrapMode: Text.WrapAnywhere
316+ elide: Text.ElideRight
317+ textFormat: Text.PlainText
318+ text: preview.e ? preview.e.text : ""
319+ font.family: preview.ty === "code" ? Theme.mono : Theme.ui
320+ font.pixelSize: preview.ty === "code" ? 14 : 17
321+ color: preview.ty === "colour" ? preview.e.text.trim() : Theme.fg
219322 }
323+ Image {
324+ id: big
325+ visible: preview.ty === "image"
326+ anchors.left: parent.left
327+ anchors.right: parent.right
328+ anchors.top: pl.bottom
329+ anchors.bottom: dims.top
330+ anchors.margins: 30
331+ fillMode: Image.PreserveAspectFit
332+ asynchronous: true
333+ sourceSize.width: 900
334+ source: preview.ty === "image" && Clip.ready[preview.e.id] ? "file://" + Clip.imageOf(preview.e) : ""
335+ }
336+ Row {
337+ id: dims
338+ visible: preview.ty === "image"
339+ x: 30
340+ anchors.bottom: parent.bottom
341+ anchors.bottomMargin: 28
342+ spacing: 12
343+ Num { anchors.baseline: df.baseline; text: preview.e && preview.ty === "image" ? preview.e.text.split(" ")[1] || "" : ""; font.pixelSize: 40 }
344+ Lb { id: df; text: preview.e && preview.ty === "image" ? preview.e.fmt : "" }
345+ }
346+ }
347+ }
348+
349+ Row {
350+ anchors.horizontalCenter: parent.horizontalCenter
351+ anchors.top: card.bottom
352+ anchors.topMargin: 28
353+ opacity: win.t
354+ spacing: 36
355+ Repeater {
356+ model: ["↑↓ select", "↵ copy", "tab filter", "del delete", "esc close"]
357+ Lb { required property string modelData; text: modelData }
220358 }
221359 }
222360 }
M .config/quickshell/aurora/Dock.qml View
@@ -1,4 +1,5 @@
11 import QtQuick
2+import QtQuick.Effects
23 import Quickshell
34 import Quickshell.Wayland
45 import Quickshell.Hyprland
@@ -13,12 +14,12 @@ PanelWindow {
1314 visible: Settings.dock
1415
1516 readonly property var hmon: Hyprland.monitorFor(modelData)
16− readonly property int size: 40
17+ readonly property int size: 48
1718 property bool shown: false
1819
1920 anchors.bottom: true
20− implicitWidth: Math.max(160, row.implicitWidth + 16)
21− implicitHeight: size + 16
21+ implicitWidth: Math.max(160, row.implicitWidth + 28)
22+ implicitHeight: size + 30
2223 exclusiveZone: 0
2324 color: "transparent"
2425 // hidden: only a 2px strip at the very bottom catches the mouse; shown: the whole dock
@@ -86,20 +87,22 @@ PanelWindow {
8687 onEntered: { hide.stop(); win.shown = true }
8788 onExited: hide.restart()
8889
90+ // a pill of round icons; running apps get dots under them (one per window, up to 3)
8991 Rectangle {
9092 id: panel
9193 width: parent.width
92− height: parent.height - 6
94+ height: parent.height - 8
9395 y: win.shown ? 0 : parent.height
94− Behavior on y { NumberAnimation { duration: 140; easing.type: Easing.OutCubic } }
95− color: Theme.bgA
96+ Behavior on y { enabled: Theme.motion; NumberAnimation { duration: Theme.t; easing.type: Easing.BezierSpline; easing.bezierCurve: Theme.glide } }
97+ radius: height / 2
98+ color: Qt.rgba(Theme.s1.r, Theme.s1.g, Theme.s1.b, Theme.panelAlpha)
9699 border.width: 1
97− border.color: Theme.accent
100+ border.color: Theme.s2
98101
99102 Row {
100103 id: row
101104 anchors.centerIn: parent
102− spacing: 4
105+ spacing: 8
103106
104107 Repeater {
105108 model: win.apps
@@ -119,43 +122,53 @@ PanelWindow {
119122
120123 Rectangle {
121124 anchors.fill: parent
122− color: cell.focused ? Theme.accentDim : (cm.containsMouse ? Theme.surfaceHi : "transparent")
125+ anchors.margins: 2
126+ radius: width / 2
127+ color: cell.focused ? Theme.fill : (cm.containsMouse ? Theme.s3 : Theme.s2)
123128 }
124129 Image {
125130 id: ico
126131 anchors.centerIn: parent
127− width: win.size - 12
132+ width: win.size - 20
128133 height: width
129134 sourceSize.width: 64
130135 sourceSize.height: 64
131136 asynchronous: true
132137 source: Quickshell.iconPath(cell.modelData.entry ? cell.modelData.entry.icon : cell.modelData.id.toLowerCase(), true)
138+ layer.enabled: Settings.appIcons !== "colour"
139+ layer.effect: Settings.appIcons === "mono" ? monoFx : dotFx
140+ Component { id: dotFx; DotMask { step: 2.5 } }
141+ Component { id: monoFx; MultiEffect { saturation: -1 } }
133142 }
134143 Text { // icon not in the theme: first letter of the app name
135144 anchors.centerIn: parent
136145 visible: ico.status !== Image.Ready
137146 text: (cell.modelData.entry ? cell.modelData.entry.name : cell.modelData.id).charAt(0).toUpperCase()
138− color: Theme.accent
139− font.family: Theme.font
140− font.pixelSize: 18
141− font.bold: true
147+ color: cell.focused ? Theme.fillFg : Theme.fg
148+ font.family: Theme.dot
149+ font.pixelSize: 26
150+ font.weight: Font.Black
142151 }
143− Rectangle { // running marker: accent underline, wider with more windows
152+ Row { // running: a dot per window (up to 3) under the icon
144153 visible: cell.running
145− anchors.bottom: parent.bottom
154+ anchors.top: parent.bottom
155+ anchors.topMargin: 3
146156 anchors.horizontalCenter: parent.horizontalCenter
147− width: Math.min(parent.width - 8, 8 + (cell.modelData.wins.length - 1) * 8)
148− height: 2
149− color: Theme.accent
157+ spacing: 3
158+ Repeater {
159+ model: Math.min(3, cell.modelData.wins.length)
160+ Rectangle { width: 4; height: 4; radius: 2; color: cell.focused ? Theme.fill : Theme.grey }
161+ }
150162 }
151163 Rectangle { // pinned marker: small dot top-right
152− visible: cell.modelData.pinned
164+ visible: cell.modelData.pinned && !cell.running
153165 anchors.top: parent.top
154166 anchors.right: parent.right
155− anchors.margins: 3
156− width: 3
157− height: 3
158− color: Theme.dim
167+ anchors.margins: 2
168+ width: 5
169+ height: 5
170+ radius: 2.5
171+ color: Theme.mute
159172 }
160173 MouseArea {
161174 id: cm
M .config/quickshell/aurora/Overview.qml View
@@ -73,7 +73,7 @@ PanelWindow {
7373
7474 Rectangle {
7575 anchors.fill: parent
76− color: Theme.bgA
76+ color: Theme.scrim
7777 MouseArea { anchors.fill: parent; onClicked: win.closeRequested() }
7878 }
7979
@@ -87,42 +87,57 @@ PanelWindow {
8787 Layout.alignment: Qt.AlignHCenter
8888 spacing: 8
8989 Rectangle {
90− implicitWidth: 420
91− implicitHeight: 32
92− color: Theme.surface
93− border.width: 1
94− border.color: Theme.accent
95− Text {
96− anchors.left: parent.left
97− anchors.leftMargin: 10
90+ implicitWidth: 520
91+ implicitHeight: 56
92+ radius: 28
93+ color: Theme.s2
94+ Icon {
95+ id: lens
96+ x: 20
9897 anchors.verticalCenter: parent.verticalCenter
9998 text: Theme.iSearch
100− color: Theme.dim
101− font.family: Theme.font
102− font.pixelSize: Theme.fontSize
99+ color: Theme.grey
100+ font.pixelSize: 19
101+ }
102+ Text {
103+ anchors.left: filter.left
104+ anchors.verticalCenter: parent.verticalCenter
105+ visible: filter.text === ""
106+ text: "Filter windows"
107+ color: Theme.mute
108+ font: filter.font
103109 }
104110 TextInput {
105111 id: filter
106− anchors.fill: parent
107− anchors.leftMargin: 32
108− anchors.rightMargin: 10
109− verticalAlignment: TextInput.AlignVCenter
112+ anchors.left: lens.right
113+ anchors.leftMargin: 14
114+ anchors.right: parent.right
115+ anchors.rightMargin: 20
116+ anchors.verticalCenter: parent.verticalCenter
110117 color: Theme.fg
111− selectionColor: Theme.accent
112− selectedTextColor: Theme.bg
113− font.family: Theme.font
114− font.pixelSize: Theme.fontSize
118+ selectionColor: Theme.fill
119+ selectedTextColor: Theme.fillFg
120+ font.family: Theme.ui
121+ font.pixelSize: 18
115122 clip: true
123+ cursorDelegate: Rectangle {
124+ width: 2
125+ color: Theme.live
126+ visible: filter.activeFocus
127+ Timer { running: filter.activeFocus; interval: 500; repeat: true; onTriggered: parent.opacity = 1 - parent.opacity }
128+ }
116129 Keys.onEscapePressed: win.closeRequested()
117130 Keys.onReturnPressed: { var t = win.firstMatch(); if (t) win.focus(t) }
118131 Keys.onEnterPressed: { var t = win.firstMatch(); if (t) win.focus(t) }
119132 }
120133 }
121− Text {
122− text: "click focus drag move enter first match esc close"
123− color: Theme.dim
124− font.family: Theme.font
125− font.pixelSize: Theme.fontSize - 2
134+ Row {
135+ Layout.leftMargin: 18
136+ spacing: 28
137+ Repeater {
138+ model: ["click focus", "drag move", "↵ first match", "esc close"]
139+ Lb { required property string modelData; text: modelData }
140+ }
126141 }
127142 }
128143
@@ -135,7 +150,7 @@ PanelWindow {
135150 readonly property int gap: 16
136151 // largest box with the monitor's aspect ratio that fits 5 x 2
137152 readonly property real boxW: Math.min((width - gap * (cols - 1)) / cols,
138− ((height - gap - 2 * 22) / 2) * win.monW / win.monH)
153+ ((height - gap - 2 * 40) / 2) * win.monW / win.monH)
139154 readonly property real boxH: boxW * win.monH / win.monW
140155 readonly property real sc: boxW / win.monW
141156
@@ -149,27 +164,36 @@ PanelWindow {
149164 readonly property bool current: win.activeWs !== null && win.activeWs.id === wsId
150165 readonly property real originX: (grid.width - (grid.boxW * grid.cols + grid.gap * (grid.cols - 1))) / 2
151166 x: originX + (index % grid.cols) * (grid.boxW + grid.gap)
152− y: Math.floor(index / grid.cols) * (grid.boxH + grid.gap + 22)
167+ y: Math.floor(index / grid.cols) * (grid.boxH + grid.gap + 40)
153168 width: grid.boxW
154− height: grid.boxH + 22
169+ height: grid.boxH + 40
155170 z: win.dragWs === wsId ? 10 : 0
156171
157− Text {
158− text: Theme.wsIcon(box.wsId) + " " + box.wsId
159− color: box.current ? Theme.accent : Theme.dim
160− font.family: Theme.font
161− font.pixelSize: Theme.fontSize
162− font.bold: box.current
172+ Row {
173+ x: 6
174+ spacing: 10
175+ Num {
176+ text: box.index + 1
177+ font.pixelSize: 28
178+ color: box.current ? Theme.fill : Theme.fg
179+ }
180+ Lb {
181+ anchors.verticalCenter: parent.verticalCenter
182+ text: win.windowsOn(box.wsId).length ? win.windowsOn(box.wsId).length + " open" : "empty"
183+ color: box.current ? Theme.fill : Theme.grey
184+ }
163185 }
164186
165187 Rectangle {
166188 id: frame
167− y: 20
189+ y: 34
168190 width: grid.boxW
169191 height: grid.boxH
170− color: Theme.surface
192+ radius: 20
193+ clip: true
194+ color: Theme.s1
171195 border.width: box.current ? 2 : 1
172− border.color: box.current ? Theme.accent : (wm.containsMouse ? Theme.accentDim : Theme.line)
196+ border.color: box.current ? Theme.fill : (wm.containsMouse ? Theme.grey : Theme.s2)
173197
174198 MouseArea {
175199 id: wm
@@ -194,9 +218,10 @@ PanelWindow {
194218 width: Math.max(12, size[0] * grid.sc)
195219 height: Math.max(12, size[1] * grid.sc)
196220 z: dm.drag.active ? 10 : (ipc.floating ? 2 : 1)
197− color: Theme.surfaceHi
221+ color: Theme.s3
222+ radius: 6
198223 border.width: dm.containsMouse || dm.drag.active ? 2 : 1
199− border.color: dm.containsMouse || dm.drag.active ? Theme.accent : Theme.line
224+ border.color: dm.containsMouse || dm.drag.active ? Theme.fill : Theme.s2
200225 opacity: hit ? 1 : 0.25
201226
202227 ScreencopyView {
M .config/quickshell/aurora/PowerMenu.qml View
@@ -1,33 +1,33 @@
11 import QtQuick
2−import QtQuick.Layouts
32 import Quickshell
43 import Quickshell.Wayland
54 import Quickshell.Hyprland
65
7−// Session / power actions (was the fuzzel `sysact` script). Type to fuzzy filter, Up/Down/Tab pick,
8−// Enter runs, Esc closes. Destructive ones need a second Enter/click ("sure?").
6+// Session / power actions (SUPER+Escape, the panel's power button). The screen goes nearly black, the actions are a
7+// column of Doto words: the selected one large with the live dot, the rest dim; uptime in a ring of dots on the right.
8+// Up/Down/Tab or hover pick, Enter/click runs, typing filters, Esc or a click on the background closes.
9+// Destructive ones need a second Enter/click within 3s (the word turns the error colour).
10+// After glyph-rice (github.com/Nebelung-cloud/glyph-rice, GPL-3.0), power/Power.qml.
911 PanelWindow {
1012 id: win
1113 property bool opened: false
1214 signal closeRequested()
1315
14− visible: opened
15− implicitWidth: 300
16− // fixed to the full list so the centred window doesn't jump while filtering
17− implicitHeight: 24 + 36 + actions.length * 32 + 22
18− exclusiveZone: 0
16+ property real t: opened ? 1 : 0
17+ Behavior on t { NumberAnimation { duration: Theme.t; easing.type: Easing.BezierSpline; easing.bezierCurve: Theme.glide } }
18+
19+ visible: t > 0
20+ anchors { top: true; bottom: true; left: true; right: true }
21+ exclusionMode: ExclusionMode.Ignore
1922 color: "transparent"
2023 WlrLayershell.layer: WlrLayer.Overlay
2124 WlrLayershell.namespace: "aurora-power"
2225 WlrLayershell.keyboardFocus: opened ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
2326
24− HyprlandFocusGrab {
25− windows: [win]
26− active: win.opened
27− onCleared: win.closeRequested()
28− }
29−
3027 readonly property var actions: Session.actions
28+ // short words for the big type (Session's labels stay for the runner)
29+ readonly property var words: ({ leave: "log out", renew: "reload", display: "screen off", shutdown: "shut down" })
30+ function word(a) { return words[a.id] || a.label }
3131
3232 property int current: 0
3333 property string armed: ""
@@ -36,7 +36,7 @@ PanelWindow {
3636 onOpenedChanged: {
3737 if (!opened) return
3838 filter.text = ""
39− current = 0
39+ current = Math.max(0, shown.indexOf(shown.filter(a => a.id === "shutdown")[0]))
4040 armed = ""
4141 filter.forceActiveFocus()
4242 }
@@ -44,10 +44,10 @@ PanelWindow {
4444 readonly property var shown: {
4545 var q = filter.text.toLowerCase().trim(), out = []
4646 for (var i = 0; i < actions.length; i++) {
47− var sc = Fuzzy.score(actions[i].label, q)
47+ var sc = Math.max(Fuzzy.score(actions[i].label, q), Fuzzy.score(word(actions[i]), q))
4848 if (sc > 0) out.push({ a: actions[i], sc: sc, i: i })
4949 }
50− out.sort(function (x, y) { return y.sc - x.sc || x.i - y.i })
50+ if (q !== "") out.sort(function (x, y) { return y.sc - x.sc || x.i - y.i })
5151 var res = []
5252 for (var j = 0; j < out.length; j++) res.push(out[j].a)
5353 return res
@@ -63,123 +63,132 @@ PanelWindow {
6363 Session.run(a.id)
6464 }
6565
66+ SystemClock { id: clock; precision: SystemClock.Minutes }
67+
6668 Rectangle {
6769 anchors.fill: parent
68− color: Theme.bgA
69− border.width: 2
70− border.color: Theme.accent
71−
72− ColumnLayout {
73− id: col
74− anchors.fill: parent
75− anchors.margins: 12
76− spacing: 2
77−
78− Rectangle {
79− Layout.fillWidth: true
80− Layout.bottomMargin: 6
81− implicitHeight: 30
82− color: Theme.surface
83− border.width: 1
84− border.color: filter.activeFocus ? Theme.accent : Theme.line
85− Text {
86− anchors.left: parent.left
87− anchors.leftMargin: 10
88− anchors.verticalCenter: parent.verticalCenter
89− text: Theme.iPower
90− color: Theme.dim
91− font.family: Theme.font
92− font.pixelSize: Theme.fontSize
93− }
94− TextInput {
95− id: filter
96− anchors.fill: parent
97− anchors.leftMargin: 32
98− anchors.rightMargin: 10
99− verticalAlignment: TextInput.AlignVCenter
100− color: Theme.fg
101− selectionColor: Theme.accent
102− selectedTextColor: Theme.bg
103− font.family: Theme.font
104− font.pixelSize: Theme.fontSize
105− clip: true
106− onTextChanged: { win.current = 0; win.armed = "" }
107− Keys.onEscapePressed: win.closeRequested()
108− Keys.onReturnPressed: win.trigger(win.current)
109− Keys.onEnterPressed: win.trigger(win.current)
110− Keys.onPressed: event => {
111− var n = win.shown.length
112− if (!n) return
113− if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab) {
114− win.current = (win.current + 1) % n; win.armed = ""; event.accepted = true
115− } else if (event.key === Qt.Key_Up || event.key === Qt.Key_Backtab) {
116− win.current = (win.current + n - 1) % n; win.armed = ""; event.accepted = true
117− }
118− }
119− }
70+ color: Theme.fade(Theme.bg, 0.96)
71+ opacity: win.t
72+ MouseArea { anchors.fill: parent; onClicked: win.closeRequested() }
73+ }
74+
75+ // typing filters; the field itself stays invisible, the filter shows as a label
76+ TextInput {
77+ id: filter
78+ width: 0
79+ height: 0
80+ opacity: 0
81+ onTextChanged: { win.current = 0; win.armed = "" }
82+ Keys.onEscapePressed: win.closeRequested()
83+ Keys.onReturnPressed: win.trigger(win.current)
84+ Keys.onEnterPressed: win.trigger(win.current)
85+ Keys.onPressed: event => {
86+ var n = win.shown.length
87+ if (!n) return
88+ if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab) {
89+ win.current = (win.current + 1) % n; win.armed = ""; event.accepted = true
90+ } else if (event.key === Qt.Key_Up || event.key === Qt.Key_Backtab) {
91+ win.current = (win.current + n - 1) % n; win.armed = ""; event.accepted = true
12092 }
93+ }
94+ }
95+
96+ // the 1920 x 1080 canvas, scaled to the screen
97+ Item {
98+ id: stage
99+ readonly property real s: Math.min(win.width / 1920, win.height / 1080)
100+ width: win.width / s
101+ height: win.height / s
102+ scale: s
103+ transformOrigin: Item.TopLeft
104+ opacity: win.t
121105
106+ Lb {
107+ x: 240
108+ y: 90
109+ visible: filter.text !== ""
110+ px: 14
111+ color: Theme.fg
112+ text: "filter · " + filter.text + (win.shown.length ? "" : " · no match")
113+ }
114+
115+ Column {
116+ x: 240 - 30 * (1 - win.t)
117+ anchors.verticalCenter: parent.verticalCenter
118+ spacing: 20
122119 Repeater {
123120 model: win.shown
124− delegate: Rectangle {
125− id: row
121+ Row {
122+ id: it
126123 required property var modelData
127124 required property int index
128− readonly property bool selected: win.current === index
125+ readonly property bool on: win.current === index
129126 readonly property bool isArmed: win.armed === modelData.id
130− Layout.fillWidth: true
131− implicitHeight: 30
132− color: isArmed ? Theme.err : (selected ? Theme.surfaceHi : (rm.containsMouse ? Theme.surface : "transparent"))
133− border.width: selected ? 1 : 0
134− border.color: isArmed ? Theme.err : Theme.accent
135−
136− Rectangle { // selection marker, like ClipWindow
137− visible: row.selected
138− width: 2
139− height: parent.height
140− color: row.isArmed ? Theme.bg : Theme.accent
141− }
142− Text {
143− id: ico
144− anchors.left: parent.left
145− anchors.leftMargin: 12
127+ spacing: 30
128+ Rectangle {
146129 anchors.verticalCenter: parent.verticalCenter
147− width: 18
148− text: row.modelData.icon
149− color: row.isArmed ? Theme.bg : Theme.accent
150− font.family: Theme.font
151− font.pixelSize: Theme.fontSize + 2
130+ visible: it.on
131+ width: 30
132+ height: 30
133+ radius: 15
134+ color: it.isArmed ? Theme.err : Theme.live
152135 }
153− Text {
154− anchors.left: ico.right
155− anchors.leftMargin: 10
156− anchors.verticalCenter: parent.verticalCenter
157− text: row.isArmed ? row.modelData.label + " sure?" : row.modelData.label
158− color: row.isArmed ? Theme.bg : Theme.fg
159− font.family: Theme.font
160− font.pixelSize: Theme.fontSize
136+ Num {
137+ text: win.word(it.modelData).toUpperCase()
138+ font.pixelSize: it.on ? 128 : 56
139+ color: it.isArmed ? Theme.err : it.on ? Theme.fg : Theme.mute
140+ Behavior on font.pixelSize {
141+ enabled: Theme.motion
142+ NumberAnimation { duration: Theme.t; easing.type: Easing.BezierSpline; easing.bezierCurve: Theme.glide }
143+ }
144+ Behavior on color { enabled: Theme.motion; ColorAnimation { duration: Theme.t } }
161145 }
162− MouseArea {
163− id: rm
164− anchors.fill: parent
165− hoverEnabled: true
166− cursorShape: Qt.PointingHandCursor
167− onEntered: if (win.current !== row.index) { win.current = row.index; win.armed = "" }
168− onClicked: win.trigger(row.index)
146+ Lb {
147+ visible: it.isArmed
148+ anchors.verticalCenter: parent.verticalCenter
149+ px: 14
150+ color: Theme.err
151+ text: "↵ again to " + win.word(it.modelData)
169152 }
153+ HoverHandler { onHoveredChanged: if (hovered && win.current !== it.index) { win.current = it.index; win.armed = "" } }
154+ TapHandler { onTapped: win.trigger(it.index) }
170155 }
171156 }
157+ }
172158
173− Item { Layout.fillHeight: true }
174−
175− Text {
176− Layout.fillWidth: true
177− Layout.topMargin: 6
178− text: win.shown.length ? "type filter enter run esc close" : "no match"
179− color: win.shown.length ? Theme.dim : Theme.err
180− font.family: Theme.font
181− font.pixelSize: Theme.fontSize - 2
159+ Item {
160+ x: stage.width - 200 - 440
161+ anchors.verticalCenter: parent.verticalCenter
162+ width: 440
163+ height: 440
164+ Ring { anchors.fill: parent; n: 60; dot: 10; f: SysStats.uptime / (6 * 3600) }
165+ Column {
166+ anchors.centerIn: parent
167+ Lb { anchors.horizontalCenter: parent.horizontalCenter; text: "uptime" }
168+ Num {
169+ anchors.horizontalCenter: parent.horizontalCenter
170+ topPadding: lead + 10
171+ bottomPadding: lead + 10
172+ text: Math.floor(SysStats.uptime / 3600) + ":" + ("0" + Math.floor(SysStats.uptime / 60) % 60).slice(-2)
173+ font.pixelSize: 104
174+ }
175+ Lb {
176+ anchors.horizontalCenter: parent.horizontalCenter
177+ readonly property int n: Hyprland.toplevels.values.length
178+ text: n + (n === 1 ? " window open" : " windows open")
179+ }
182180 }
183181 }
182+
183+ Row {
184+ x: 240
185+ anchors.bottom: parent.bottom
186+ anchors.bottomMargin: 70
187+ spacing: 34
188+ Lb { text: "↑↓ select" }
189+ Lb { text: "↵ confirm" }
190+ Lb { text: "type to filter" }
191+ Lb { text: "esc cancel" }
192+ }
184193 }
185194 }
M .config/quickshell/aurora/Rec.qml View
@@ -56,7 +56,7 @@ Singleton {
5656 }
5757 Process {
5858 id: selectProc
59− command: ["slurp"]
59+ command: ["slurp"].concat(Theme.slurpArgs)
6060 // slurp prints nothing when cancelled (Esc, or stop() killing it)
6161 stdout: StdioCollector {
6262 onStreamFinished: {
M .config/quickshell/aurora/Runner.qml View
@@ -1,5 +1,6 @@
11 import QtQuick
22 import QtQuick.Layouts
3+import QtQuick.Effects
34 import Quickshell
45 import Quickshell.Io
56 import Quickshell.Wayland
@@ -9,6 +10,8 @@ import Quickshell.Hyprland
910 // aurora commands and every executable on $PATH, ranked by match and launch count.
1011 // `=expr` calculator (Enter copies), `>cmd` runs a shell command. Ctrl+Enter runs in the terminal.
1112 // dmenu mode (`aurora-run -d`, `dmenu` IPC): plain list from the script's stdin, the choice goes back over a fifo.
13+// Looks like glyph's launcher (github.com/Nebelung-cloud/glyph-rice, GPL-3.0, launcher/Launcher.qml): a card over the
14+// dimmed screen, a search pill with the live caret, mode chips (all / run = `>` / calc = `=`), round icons in rows.
1215 PanelWindow {
1316 id: win
1417 property bool opened: false
@@ -16,7 +19,7 @@ PanelWindow {
1619 signal closeRequested()
1720 signal openRequested() // a dmenu request is loaded and wants the window
1821
19− readonly property int rowH: 36
22+ readonly property int rowH: 60
2023 readonly property int rows: 10
2124 readonly property int maxShown: 60
2225 readonly property string home: Quickshell.env("HOME")
@@ -92,21 +95,17 @@ PanelWindow {
9295 filter.text = ""
9396 }
9497
95− visible: opened
96− implicitWidth: dmenu ? 640 : 560
97− implicitHeight: 24 + 36 + rowsShown * rowH + 22
98− exclusiveZone: 0
98+ property real t: opened ? 1 : 0
99+ Behavior on t { NumberAnimation { duration: Theme.t; easing.type: Easing.BezierSpline; easing.bezierCurve: Theme.glide } }
100+
101+ visible: t > 0
102+ anchors { top: true; bottom: true; left: true; right: true }
103+ exclusionMode: ExclusionMode.Ignore
99104 color: "transparent"
100105 WlrLayershell.layer: WlrLayer.Overlay
101106 WlrLayershell.namespace: "aurora-runner"
102107 WlrLayershell.keyboardFocus: opened ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
103108
104− HyprlandFocusGrab {
105− windows: [win]
106− active: win.opened
107− onCleared: win.closeRequested()
108− }
109−
110109 property int current: 0
111110 property string armed: ""
112111 Timer { id: disarm; interval: 3000; onTriggered: win.armed = "" }
@@ -372,193 +371,255 @@ PanelWindow {
372371 }
373372
374373 // ---- UI ----
374+ readonly property string modeNow: filter.text.charAt(0) === ">" ? "run" : filter.text.charAt(0) === "=" ? "calc" : "all"
375+ function setMode(m) {
376+ var t = filter.text.replace(/^[>=]\s*/, "")
377+ filter.text = m === "run" ? ">" + t : m === "calc" ? "=" + t : t
378+ filter.forceActiveFocus()
379+ }
380+
375381 Rectangle {
376382 anchors.fill: parent
377− color: Theme.bgA
378− border.width: 2
379− border.color: Theme.accent
383+ color: Theme.scrim
384+ opacity: win.t
385+ MouseArea { anchors.fill: parent; onClicked: win.closeRequested() }
386+ }
380387
381− ColumnLayout {
382− anchors.fill: parent
383− anchors.margins: 12
384− spacing: 2
388+ Rectangle {
389+ id: card
390+ anchors.horizontalCenter: parent.horizontalCenter
391+ y: Math.round(parent.height * 0.13) + 24 * (1 - win.t)
392+ width: Math.min(parent.width - 80, win.dmenu ? 760 : 860)
393+ height: 30 + 66 + (win.rowsShown > 0 ? 20 + win.rowsShown * win.rowH : 0) + 30
394+ opacity: win.t
395+ radius: 44
396+ color: Qt.rgba(Theme.s1.r, Theme.s1.g, Theme.s1.b, Theme.panelAlpha)
397+ border.width: 1
398+ border.color: Theme.s2
399+ MouseArea { anchors.fill: parent }
385400
386− Rectangle {
387− Layout.fillWidth: true
388− Layout.bottomMargin: 6
389− implicitHeight: 30
390− color: Theme.surface
391− border.width: 1
392− border.color: filter.activeFocus ? Theme.accent : Theme.line
393− Text {
394− id: promptText
395− anchors.left: parent.left
396− anchors.leftMargin: 10
397− anchors.verticalCenter: parent.verticalCenter
398− text: win.dmenu && win.dmPrompt !== "" ? win.dmPrompt : (win.dmPassword ? Theme.iLock : Theme.iSearch)
399− color: win.dmenu && win.dmPrompt !== "" ? Theme.accent : Theme.dim
400− font.family: Theme.font
401− font.pixelSize: Theme.fontSize
401+ // ---- search pill ----
402+ Rectangle {
403+ id: search
404+ x: 30
405+ y: 30
406+ width: parent.width - 60
407+ height: 66
408+ radius: 33
409+ color: Theme.s2
410+
411+ Rectangle { // the dmenu prompt as a chip, else the lens / lock
412+ id: promptBox
413+ x: 12
414+ anchors.verticalCenter: parent.verticalCenter
415+ visible: win.dmenu && win.dmPrompt !== ""
416+ width: visible ? promptText.implicitWidth + 28 : 0
417+ height: 42
418+ radius: 21
419+ color: Theme.fill
420+ Lb { id: promptText; anchors.centerIn: parent; text: win.dmPrompt.replace(/[:>]\s*$/, ""); color: Theme.fillFg }
421+ }
422+ Icon {
423+ id: lens
424+ x: 24
425+ anchors.verticalCenter: parent.verticalCenter
426+ visible: !promptBox.visible
427+ text: win.dmPassword ? Theme.iLock : Theme.iSearch
428+ color: Theme.grey
429+ font.pixelSize: 22
430+ }
431+ TextInput {
432+ id: filter
433+ anchors.left: promptBox.visible ? promptBox.right : lens.right
434+ anchors.leftMargin: 16
435+ anchors.right: chips.visible ? chips.left : parent.right
436+ anchors.rightMargin: 16
437+ anchors.verticalCenter: parent.verticalCenter
438+ echoMode: win.dmPassword ? TextInput.Password : TextInput.Normal
439+ passwordCharacter: "•"
440+ color: Theme.fg
441+ selectionColor: Theme.fill
442+ selectedTextColor: Theme.fillFg
443+ font.family: win.dmPassword ? Theme.mono : Theme.ui
444+ font.pixelSize: 22
445+ font.letterSpacing: win.dmPassword ? 3 : 0
446+ clip: true
447+ cursorDelegate: Rectangle {
448+ width: 2
449+ color: Theme.live
450+ visible: filter.activeFocus
451+ Timer { running: filter.activeFocus; interval: 500; repeat: true; onTriggered: parent.opacity = 1 - parent.opacity }
402452 }
403453 Text {
404− anchors.left: filter.left
405− anchors.verticalCenter: parent.verticalCenter
406− visible: filter.text === "" && win.dmenu && win.dmPlaceholder !== ""
407− text: win.dmPlaceholder
408− color: Theme.dim
409− font.family: Theme.font
410− font.pixelSize: Theme.fontSize
454+ visible: filter.text === ""
455+ text: win.dmenu ? (win.dmPlaceholder !== "" ? win.dmPlaceholder : (win.dmPassword ? "Password" : "Type to filter"))
456+ : "Search apps, commands, =calc, >run"
457+ font.family: Theme.ui
458+ font.pixelSize: 22
459+ color: Theme.mute
411460 }
412− TextInput {
413− id: filter
414− anchors.fill: parent
415− anchors.leftMargin: Math.max(32, promptText.implicitWidth + 20)
416− anchors.rightMargin: 10
417− verticalAlignment: TextInput.AlignVCenter
418− echoMode: win.dmPassword ? TextInput.Password : TextInput.Normal
419− passwordCharacter: "*"
420− color: Theme.fg
421− selectionColor: Theme.accent
422− selectedTextColor: Theme.bg
423− font.family: Theme.font
424− font.pixelSize: Theme.fontSize
425− clip: true
426− onTextChanged: { win.current = 0; win.armed = "" }
427− Keys.onEscapePressed: win.closeRequested()
428− Keys.onReturnPressed: event => win.trigger(win.current, event.modifiers & Qt.ControlModifier)
429− Keys.onEnterPressed: event => win.trigger(win.current, event.modifiers & Qt.ControlModifier)
430− Keys.onPressed: event => {
431− var n = win.shown.length
432− if (!n) return
433− var step = 0
434− if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab) step = 1
435− else if (event.key === Qt.Key_Up || event.key === Qt.Key_Backtab) step = -1
436− else if (event.key === Qt.Key_PageDown) step = Math.max(2, win.rowsShown)
437− else if (event.key === Qt.Key_PageUp) step = -Math.max(2, win.rowsShown)
438− else if ((event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_N || event.key === Qt.Key_J)) step = 1
439− else if ((event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_P || event.key === Qt.Key_K)) step = -1
440− if (step === 0) return
441− if (Math.abs(step) === 1) win.current = (win.current + step + n) % n
442− else win.current = Math.max(0, Math.min(n - 1, win.current + step))
443− win.armed = ""
444− event.accepted = true
461+ onTextChanged: { win.current = 0; win.armed = "" }
462+ Keys.onEscapePressed: win.closeRequested()
463+ Keys.onReturnPressed: event => win.trigger(win.current, event.modifiers & Qt.ControlModifier)
464+ Keys.onEnterPressed: event => win.trigger(win.current, event.modifiers & Qt.ControlModifier)
465+ Keys.onPressed: event => {
466+ var n = win.shown.length
467+ if (!n) return
468+ var step = 0
469+ if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab) step = 1
470+ else if (event.key === Qt.Key_Up || event.key === Qt.Key_Backtab) step = -1
471+ else if (event.key === Qt.Key_PageDown) step = Math.max(2, win.rowsShown)
472+ else if (event.key === Qt.Key_PageUp) step = -Math.max(2, win.rowsShown)
473+ else if ((event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_N || event.key === Qt.Key_J)) step = 1
474+ else if ((event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_P || event.key === Qt.Key_K)) step = -1
475+ if (step === 0) return
476+ if (Math.abs(step) === 1) win.current = (win.current + step + n) % n
477+ else win.current = Math.max(0, Math.min(n - 1, win.current + step))
478+ win.armed = ""
479+ event.accepted = true
480+ }
481+ }
482+ Row {
483+ id: chips
484+ visible: !win.dmenu
485+ anchors.right: parent.right
486+ anchors.rightMargin: 12
487+ anchors.verticalCenter: parent.verticalCenter
488+ spacing: 6
489+ Repeater {
490+ model: ["all", "run", "calc"]
491+ Chip {
492+ required property string modelData
493+ text: modelData
494+ selected: win.modeNow === modelData
495+ onClicked: win.setMode(modelData)
445496 }
446497 }
447498 }
499+ }
448500
449− ListView {
450− id: list
451− Layout.fillWidth: true
452− Layout.fillHeight: true
453− visible: win.rowsShown > 0
454− clip: true
455− spacing: 0
456− boundsBehavior: Flickable.StopAtBounds
457− model: win.shown
458− currentIndex: win.current
459− highlightFollowsCurrentItem: false
460− onCurrentIndexChanged: positionViewAtIndex(currentIndex, ListView.Contain)
461− delegate: Rectangle {
462− id: row
463− required property var modelData
464− required property int index
465− readonly property bool selected: win.current === index
466− readonly property bool isArmed: win.armed !== "" && win.armed === modelData.key
467− width: list.width
468− height: win.rowH
469− color: isArmed ? Theme.err : (selected ? Theme.surfaceHi : (rm.containsMouse ? Theme.surface : "transparent"))
470− border.width: selected ? 1 : 0
471− border.color: isArmed ? Theme.err : Theme.accent
501+ ListView {
502+ id: list
503+ x: 30
504+ anchors.top: search.bottom
505+ anchors.topMargin: 20
506+ width: parent.width - 60
507+ height: win.rowsShown * win.rowH
508+ visible: win.rowsShown > 0
509+ clip: true
510+ spacing: 0
511+ boundsBehavior: Flickable.StopAtBounds
512+ model: win.shown
513+ currentIndex: win.current
514+ highlightFollowsCurrentItem: false
515+ onCurrentIndexChanged: positionViewAtIndex(currentIndex, ListView.Contain)
516+ delegate: Item {
517+ id: row
518+ required property var modelData
519+ required property int index
520+ readonly property bool selected: win.current === index
521+ readonly property bool isArmed: win.armed !== "" && win.armed === modelData.key
522+ readonly property bool isDm: modelData.kind === "dm"
523+ readonly property color ink: isArmed ? Theme.errFg : selected ? Theme.fillFg : Theme.fg
524+ width: list.width
525+ height: win.rowH
472526
473− Rectangle { // selection marker, like ClipWindow
474− visible: row.selected
475− width: 2
476− height: parent.height
477− color: row.isArmed ? Theme.bg : Theme.accent
478− }
479− Item {
480− id: icoBox
481− anchors.left: parent.left
482− anchors.leftMargin: row.modelData.kind === "dm" ? 2 : 10
483− anchors.verticalCenter: parent.verticalCenter
484− visible: row.modelData.kind !== "dm"
485− width: visible ? 24 : 0
486− height: 24
487− Image {
488− id: img
489− anchors.fill: parent
490− visible: row.modelData.icon !== "" && status === Image.Ready
491− source: row.modelData.icon
492− sourceSize.width: 48
493− sourceSize.height: 48
494− asynchronous: true
495− smooth: true
496− }
497− Text {
498− anchors.centerIn: parent
499− visible: !img.visible
500− text: row.modelData.glyph
501− color: row.isArmed ? Theme.bg : Theme.accent
502− font.family: Theme.font
503− font.pixelSize: Theme.fontSize + 4
504− }
527+ Rectangle {
528+ anchors.fill: parent
529+ anchors.topMargin: 2
530+ anchors.bottomMargin: 2
531+ radius: height / 2
532+ color: row.isArmed ? Theme.err : row.selected ? Theme.fill : rm.containsMouse ? Theme.s2 : "transparent"
533+ }
534+ Rectangle {
535+ id: icoBox
536+ x: 7
537+ anchors.verticalCenter: parent.verticalCenter
538+ visible: !row.isDm
539+ width: visible ? 44 : 0
540+ height: 44
541+ radius: 22
542+ color: row.selected || row.isArmed ? Theme.fade(row.ink, 0.14) : Theme.s2
543+ Image {
544+ id: img
545+ anchors.centerIn: parent
546+ width: 26
547+ height: 26
548+ visible: row.modelData.icon !== "" && status === Image.Ready
549+ source: row.modelData.icon
550+ sourceSize: Qt.size(52, 52)
551+ asynchronous: true
552+ smooth: true
553+ layer.enabled: Settings.appIcons !== "colour"
554+ layer.effect: Settings.appIcons === "mono" ? monoFx : dotFx
555+ Component { id: dotFx; DotMask { step: 2 } }
556+ Component { id: monoFx; MultiEffect { saturation: -1 } }
505557 }
506− Text {
507− id: tag
508− anchors.right: parent.right
509− anchors.rightMargin: 10
510− anchors.verticalCenter: parent.verticalCenter
511− text: row.isArmed ? "sure?" : row.modelData.tag
512− color: row.isArmed ? Theme.bg : Theme.dim
513− font.family: Theme.font
514− font.pixelSize: Theme.fontSize - 2
558+ Icon {
559+ anchors.centerIn: parent
560+ visible: !img.visible
561+ text: row.modelData.glyph
562+ color: row.ink
563+ font.pixelSize: 19
515564 }
565+ }
566+ Lb {
567+ id: tag
568+ anchors.right: parent.right
569+ anchors.rightMargin: 22
570+ anchors.verticalCenter: parent.verticalCenter
571+ px: 10
572+ text: row.isArmed ? "↵ again · sure?" : row.modelData.tag
573+ color: row.isArmed || row.selected ? row.ink : Theme.grey
574+ }
575+ Column {
576+ anchors.left: icoBox.right
577+ anchors.leftMargin: row.isDm ? 22 : 14
578+ anchors.right: tag.left
579+ anchors.rightMargin: 14
580+ anchors.verticalCenter: parent.verticalCenter
516581 Text {
517− id: nameText
518− anchors.left: icoBox.right
519− anchors.leftMargin: 10
520− anchors.verticalCenter: parent.verticalCenter
521− width: Math.min(implicitWidth, tag.x - x - 12)
582+ width: parent.width
522583 elide: Text.ElideRight
523584 text: row.modelData.name
524− color: row.isArmed ? Theme.bg : Theme.fg
525− font.family: Theme.font
526− font.pixelSize: Theme.fontSize
527− font.bold: row.modelData.kind === "calc"
585+ color: row.ink
586+ font.family: row.modelData.kind === "calc" ? Theme.mono : Theme.ui
587+ font.pixelSize: row.modelData.kind === "calc" ? 20 : 15
588+ font.weight: row.modelData.kind === "calc" ? Font.Bold : Font.DemiBold
528589 }
529− Text {
530− anchors.left: nameText.right
531− anchors.leftMargin: 10
532− anchors.right: tag.left
533− anchors.rightMargin: 12
534− anchors.verticalCenter: parent.verticalCenter
590+ Lb {
591+ visible: row.modelData.sub !== ""
592+ width: parent.width
535593 elide: Text.ElideRight
594+ px: 10
536595 text: row.modelData.sub
537− color: row.isArmed ? Theme.bg : Theme.dim
538− font.family: Theme.font
539− font.pixelSize: Theme.fontSize - 2
540− }
541− MouseArea {
542− id: rm
543− anchors.fill: parent
544− hoverEnabled: true
545− cursorShape: Qt.PointingHandCursor
546− onEntered: if (win.current !== row.index) { win.current = row.index; win.armed = "" }
547− onClicked: win.trigger(row.index, false)
596+ color: row.selected || row.isArmed ? row.ink : Theme.grey
597+ opacity: row.selected ? 0.75 : 1
548598 }
549599 }
600+ MouseArea {
601+ id: rm
602+ anchors.fill: parent
603+ hoverEnabled: true
604+ cursorShape: Qt.PointingHandCursor
605+ onEntered: if (win.current !== row.index) { win.current = row.index; win.armed = "" }
606+ onClicked: win.trigger(row.index, false)
607+ }
550608 }
609+ }
610+ }
551611
552− Text {
553− Layout.fillWidth: true
554− Layout.topMargin: 6
555− text: !win.dmenu ? (win.shown.length ? "enter run ctrl+enter terminal =calc >cmd esc close" : "no match")
556− : win.rowsShown === 0 ? "enter ok esc cancel"
557− : win.shown.length ? "enter select ctrl+enter typed text esc cancel" : "no match enter typed text esc cancel"
558− color: win.shown.length || win.dmenu ? Theme.dim : Theme.err
559− font.family: Theme.font
560− font.pixelSize: Theme.fontSize - 2
561− }
612+ Row {
613+ anchors.horizontalCenter: parent.horizontalCenter
614+ anchors.top: card.bottom
615+ anchors.topMargin: 26
616+ opacity: win.t
617+ spacing: 34
618+ Repeater {
619+ model: !win.dmenu ? (win.shown.length ? ["↑↓ select", "↵ run", "ctrl ↵ terminal", "=calc", ">cmd", "esc close"] : ["no match", "esc close"])
620+ : win.rowsShown === 0 ? ["↵ ok", "esc cancel"]
621+ : win.shown.length ? ["↑↓ select", "↵ choose", "ctrl ↵ typed text", "esc cancel"] : ["no match", "↵ typed text", "esc cancel"]
622+ Lb { required property string modelData; text: modelData }
562623 }
563624 }
564625 }
M .config/quickshell/aurora/Shot.qml View
@@ -17,7 +17,7 @@ Singleton {
1717 function take(mode) {
1818 var script
1919 if (mode === "ocr") {
20− script = 'slurp | grim -g - - | tesseract stdin stdout -l "$2" | wl-copy'
20+ script = 'slurp ' + Theme.slurpArgs.join(" ") + ' | grim -g - - | tesseract stdin stdout -l "$2" | wl-copy'
2121 + ' && notify-send -a screenshot "OCR" "text copied to clipboard"'
2222 } else if (names[mode] === undefined) {
2323 return
M .config/quickshell/aurora/Theme.qml View
@@ -90,6 +90,8 @@ Singleton {
9090
9191 function fade(c, a) { return Qt.rgba(c.r, c.g, c.b, a) }
9292 function hex(c) { return c.toString().slice(1, 7).toUpperCase() }
93+ // slurp in the theme: the screen dims, the selection gets a 1px primary outline (Rec, Shot's OCR)
94+ readonly property var slurpArgs: ["-b", "#" + hex(bg) + "88", "-c", "#" + hex(fill) + "ff", "-s", "#00000000", "-w", "1"]
9395 // hours for the 12/24h setting: "09" or "9"
9496 function hh(d) {
9597 var h = d.getHours()
@@ -235,6 +237,9 @@ Singleton {
235237 readonly property string iBolt: cp(0xF140B) // lightning-bolt (charging)
236238 readonly property string iDots: cp(0xF01D8) // dots-horizontal
237239 readonly property string iDashboard: cp(0xF056E) // view-dashboard (overview)
240+ readonly property string iText: cp(0xF0262) // format-align-left
241+ readonly property string iLink: cp(0xF0339) // link-variant
242+ readonly property string iCode: cp(0xF0169) // code-braces
238243
239244 // workspace icons: the exact Font Awesome glyphs from your waybar config.
240245 // 1 browser, 2 code, 3 book, 4 cog, 5 chat, 6 file, 7 folder, 8 terminal, 9 flask, 10 image