Merge branch 'omacom:quattro' into omarchycn
This commit is contained in:
43 files changed
+1162
-158
No files matched your search
+57
-24
@@ -3,12 +3,9 @@ import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Wayland
|
||||
|
||||
// A fullscreen overlay whose surface outlives each open. A fresh surface draws
|
||||
// its first frames before the compositor sends its fractional scale, so an
|
||||
// overlay mapped per open flashed blurry until the scale arrived. Closed, the
|
||||
// surface parks as a 1x1, input-less, content-less layer below windows: small
|
||||
// enough to cost nothing, off the overlay layer so it never blocks direct
|
||||
// scanout for fullscreen apps. Opening only resizes and raises it.
|
||||
// Map a fresh fullscreen surface on each open. Keep its first frames
|
||||
// transparent until Qt receives the output's scale, so it opens sharp without
|
||||
// retaining a parked surface that can be closed when a monitor disconnects.
|
||||
PanelWindow {
|
||||
id: window
|
||||
|
||||
@@ -17,11 +14,31 @@ PanelWindow {
|
||||
property int shownLayer: WlrLayer.Overlay
|
||||
property int shownKeyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
|
||||
// The surface no longer lands on the focused output by being mapped there,
|
||||
// so it follows the focused monitor each time it is shown. Unset until the
|
||||
// first show lets the compositor choose.
|
||||
property var targetScreen: null
|
||||
property Region emptyRegion: Region {}
|
||||
readonly property var targetMonitor: {
|
||||
var monitors = Hyprland.monitors.values
|
||||
for (var i = 0; i < monitors.length; i++) {
|
||||
if (targetScreen && monitors[i].name === targetScreen.name) return monitors[i]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Wait for fullscreen geometry as well as scale so no smaller opening frame
|
||||
// is stretched across the output. Fractional scale uses units of 1/120;
|
||||
// compare in those units to tolerate floating point rounding.
|
||||
readonly property bool contentReady: shown && backingWindowVisible && !!targetScreen
|
||||
&& width === targetScreen.width && height === targetScreen.height
|
||||
&& !!targetMonitor && Math.round(devicePixelRatio * 120) === Math.round(targetMonitor.scale * 120)
|
||||
property bool contentRevealed: false
|
||||
|
||||
onContentReadyChanged: {
|
||||
contentRevealed = false
|
||||
// Resize and scale notifications arrive while Qt is still updating the
|
||||
// window. Reveal on the next turn, once its content layout has caught up.
|
||||
if (contentReady) Qt.callLater(function() {
|
||||
if (window.contentReady) window.contentRevealed = true
|
||||
})
|
||||
}
|
||||
|
||||
function focusedScreen() {
|
||||
var monitor = Hyprland.focusedMonitor
|
||||
@@ -29,28 +46,44 @@ PanelWindow {
|
||||
for (var i = 0; i < Quickshell.screens.length; i++) {
|
||||
if (Quickshell.screens[i].name === name) return Quickshell.screens[i]
|
||||
}
|
||||
return null
|
||||
return Quickshell.screens.length > 0 ? Quickshell.screens[0] : null
|
||||
}
|
||||
|
||||
onShownChanged: if (shown) targetScreen = focusedScreen() || targetScreen
|
||||
// Live monitor scaling does not emit a Hyprland monitor event, so its cached
|
||||
// scale can lag behind Qt. Refresh on opening and on scale changes while open.
|
||||
onShownChanged: {
|
||||
targetScreen = shown ? focusedScreen() : null
|
||||
if (shown) Hyprland.refreshMonitors()
|
||||
}
|
||||
onDevicePixelRatioChanged: if (shown) Hyprland.refreshMonitors()
|
||||
|
||||
visible: true
|
||||
Connections {
|
||||
target: Quickshell
|
||||
function onScreensChanged() {
|
||||
if (window.shown && Quickshell.screens.indexOf(window.targetScreen) < 0) {
|
||||
window.targetScreen = null
|
||||
// Let the removed output finish closing its old window before mapping
|
||||
// a replacement, rather than recreating it during Qt's teardown.
|
||||
Qt.callLater(function() {
|
||||
if (window.shown && !window.targetScreen) window.targetScreen = window.focusedScreen()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visible: shown && !!targetScreen && !!targetMonitor
|
||||
screen: targetScreen
|
||||
anchors { top: true; left: true; bottom: shown; right: shown }
|
||||
implicitWidth: 1
|
||||
implicitHeight: 1
|
||||
mask: shown ? null : emptyRegion
|
||||
anchors { top: true; left: true; bottom: true; right: true }
|
||||
color: "transparent"
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: shown ? shownLayer : WlrLayer.Bottom
|
||||
WlrLayershell.keyboardFocus: shown ? shownKeyboardFocus : WlrKeyboardFocus.None
|
||||
WlrLayershell.layer: shownLayer
|
||||
WlrLayershell.keyboardFocus: shownKeyboardFocus
|
||||
|
||||
// Draw nothing until the surface has actually grown. A frame drawn while it
|
||||
// is still 1x1 holds only the scrim's color, which the compositor would
|
||||
// stretch across the whole screen until the fullscreen frame arrives.
|
||||
// Opacity preserves keyboard handling while the scale arrives, including
|
||||
// search keystrokes and Escape pressed immediately after opening.
|
||||
Binding {
|
||||
target: window.contentItem
|
||||
property: "visible"
|
||||
value: window.shown && window.width > 1 && window.height > 1
|
||||
property: "opacity"
|
||||
value: window.contentRevealed ? 1 : 0
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,9 @@ Every subscription on one page, limits first.
|
||||
per limit window: its meter and the time until it resets (the exact percentage
|
||||
on hover). A model-scoped allowance on the same clock (Claude's Fable weekly
|
||||
limit) is a tick on that window's meter rather than a line of its own; the
|
||||
row's tooltip names it. Sign-in and endpoint trouble shows under the name in the urgent
|
||||
color. Limits kept from an earlier check after a failed one dim, and their
|
||||
row's tooltip names it. A lapsed or missing sign-in shows a _Sign-in required_ link that signs
|
||||
that account in again from the panel; other endpoint trouble shows under the
|
||||
name in the urgent color. Limits kept from an earlier check after a failed one dim, and their
|
||||
tooltip says how old they are.
|
||||
- **Accounts** — an agent with more than one subscription account (see
|
||||
`omarchy agent account`) lists each: name and plan on one line (the email on hover), and its own limit
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
|
||||
BarIndicator {
|
||||
id: root
|
||||
|
||||
property bool granted: false
|
||||
property bool refreshPending: false
|
||||
|
||||
active: granted
|
||||
activeText: ""
|
||||
inactiveText: ""
|
||||
activeTooltipText: "Disable Passwordless Sudo"
|
||||
inactiveTooltipText: "Passwordless Sudo"
|
||||
useActiveColor: true
|
||||
activeColor: Color.urgent
|
||||
|
||||
function refresh() {
|
||||
if (!root.bar) return
|
||||
if (statusProc.running) {
|
||||
root.refreshPending = true
|
||||
return
|
||||
}
|
||||
root.refreshPending = false
|
||||
statusProc.running = true
|
||||
}
|
||||
|
||||
onBarChanged: refresh()
|
||||
Component.onCompleted: refresh()
|
||||
|
||||
Connections {
|
||||
target: root.indicatorHost
|
||||
ignoreUnknownSignals: true
|
||||
function onRefreshRequested() { root.refresh() }
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 5000
|
||||
repeat: true
|
||||
running: !!root.bar
|
||||
onTriggered: root.refresh()
|
||||
}
|
||||
|
||||
Process {
|
||||
id: statusProc
|
||||
command: ["omarchy-sudo-passwordless", "--active"]
|
||||
onExited: function(exitCode, exitStatus) {
|
||||
root.granted = exitCode === 0 && exitStatus === 0
|
||||
if (root.refreshPending) Qt.callLater(root.refresh)
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: disableProc
|
||||
command: ["omarchy-sudo-passwordless", "--disable"]
|
||||
onExited: function(exitCode, exitStatus) {
|
||||
if ((exitCode !== 0 || exitStatus !== 0) && root.bar)
|
||||
root.bar.run('omarchy-notification-send "Could not disable passwordless sudo" "Check the sudo configuration and try again."')
|
||||
if (root.indicatorHost) root.indicatorHost.refresh()
|
||||
else root.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: function() {
|
||||
if (!root.bar || disableProc.running) return
|
||||
if (root.granted) disableProc.running = true
|
||||
else root.bar.run("omarchy-launch-floating-terminal-with-presentation omarchy-sudo-passwordless")
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,11 @@
|
||||
"placeholderText": "Search indicators...",
|
||||
"emptyText": "No indicators",
|
||||
"options": [
|
||||
{
|
||||
"value": "PasswordlessSudo",
|
||||
"label": "Passwordless sudo",
|
||||
"description": "Warning while passwordless root access is enabled"
|
||||
},
|
||||
{
|
||||
"value": "Dictation",
|
||||
"label": "Dictation",
|
||||
|
||||
@@ -8,7 +8,7 @@ BarWidget {
|
||||
id: root
|
||||
moduleName: "omarchy.indicators"
|
||||
|
||||
readonly property var defaultIndicatorEntries: [ "Dictation", "ScreenRecording", "Reminder", "NightLight", "Dnd", "StayAwake" ]
|
||||
readonly property var defaultIndicatorEntries: [ "PasswordlessSudo", "Dictation", "ScreenRecording", "Reminder", "NightLight", "Dnd", "StayAwake" ]
|
||||
readonly property var indicatorEntries: indicatorEntriesFromSettings(settings)
|
||||
property var activeIndicatorIds: []
|
||||
property var indicatorActiveStates: ({})
|
||||
|
||||
@@ -1088,7 +1088,7 @@ Item {
|
||||
// then on the card grows and shrinks downward instead of re-centering
|
||||
// on every resize, which made the menu jump around. The rows height is
|
||||
// frozen at the same moment, so the starting menu also caps how tall the
|
||||
// card may grow from there. Closing unfreezes both.
|
||||
// card may grow from there. Closing or changing screens unfreezes both.
|
||||
property int cardTop: -1
|
||||
property int maxRowsHeight: -1
|
||||
readonly property int centeredTop: Math.max(Style.gapsOut, Math.round((height - root.cardHeight) / 2))
|
||||
@@ -1099,8 +1099,8 @@ Item {
|
||||
maxRowsHeight = root.visibleRowsHeight
|
||||
}
|
||||
}
|
||||
// The surface stays mapped between opens, so closing is shown going false.
|
||||
onShownChanged: if (!shown) { cardTop = -1; maxRowsHeight = -1 }
|
||||
onTargetScreenChanged: { cardTop = -1; maxRowsHeight = -1 }
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
Reference in new issue
Block a user