Merge remote-tracking branch 'origin/omarchy-shell' into omarchy-4
# Conflicts: # bin/omarchy-debug # bin/omarchy-upload-log
This commit is contained in:
@@ -1257,6 +1257,12 @@ Item {
|
||||
readonly property bool hovered: moduleHover.hovered
|
||||
readonly property bool dragSource: root.barDragSource === slot
|
||||
readonly property bool panelOpen: root.activePopout === slot.activeItem
|
||||
readonly property real openIndicatorInlineOffset: {
|
||||
var item = slot.activeItem
|
||||
if (!item || !("openIndicatorInlineOffset" in item)) return 0
|
||||
var offset = Number(item.openIndicatorInlineOffset)
|
||||
return isFinite(offset) ? offset : 0
|
||||
}
|
||||
|
||||
implicitWidth: activeItem && activeItem.visible ? (root.vertical ? root.barSize : activeItem.implicitWidth) : 0
|
||||
implicitHeight: activeItem && activeItem.visible ? activeItem.implicitHeight : 0
|
||||
@@ -1329,8 +1335,12 @@ Item {
|
||||
radius: Math.min(width, height) / 2
|
||||
width: root.vertical ? Style.space(2) : Math.max(Style.space(10), Math.round(parent.width * 0.55))
|
||||
height: root.vertical ? Math.max(Style.space(10), Math.round(parent.height * 0.55)) : Style.space(2)
|
||||
x: root.vertical ? (root.position === "left" ? parent.width - width - inset : inset) : Math.round((parent.width - width) / 2)
|
||||
y: root.vertical ? Math.round((parent.height - height) / 2) : (root.position === "top" ? parent.height - height - inset : inset)
|
||||
x: root.vertical
|
||||
? (root.position === "left" ? parent.width - width - inset : inset)
|
||||
: (slot.openIndicatorInlineOffset === 0 ? Math.round((parent.width - width) / 2) : (parent.width - width) / 2 + slot.openIndicatorInlineOffset)
|
||||
y: root.vertical
|
||||
? (slot.openIndicatorInlineOffset === 0 ? Math.round((parent.height - height) / 2) : (parent.height - height) / 2 + slot.openIndicatorInlineOffset)
|
||||
: (root.position === "top" ? parent.height - height - inset : inset)
|
||||
z: 50
|
||||
|
||||
Behavior on opacity {
|
||||
|
||||
@@ -35,14 +35,17 @@ BarWidget {
|
||||
root.bar.run("hyprctl dispatch " + Util.shellQuote("hl.dsp.focus({ workspace = \"" + id + "\" })"))
|
||||
}
|
||||
|
||||
implicitWidth: grid.implicitWidth
|
||||
readonly property real trailingGap: root.vertical ? 0 : Style.spaceReal(1.5)
|
||||
|
||||
implicitWidth: grid.implicitWidth + trailingGap
|
||||
implicitHeight: grid.implicitHeight
|
||||
|
||||
GridLayout {
|
||||
id: grid
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: root.trailingGap
|
||||
columns: root.vertical ? 1 : root.workspaceIds().length
|
||||
columnSpacing: root.vertical ? 0 : Style.space(2)
|
||||
columnSpacing: root.vertical ? 0 : Style.space(1)
|
||||
rowSpacing: root.vertical ? Style.space(2) : 0
|
||||
|
||||
Repeater {
|
||||
@@ -60,7 +63,7 @@ BarWidget {
|
||||
opacity: occupied || focused ? 1 : 0.5
|
||||
horizontalMargin: 6
|
||||
verticalPadding: 6
|
||||
fixedWidth: root.vertical ? root.barSize : Style.space(22)
|
||||
fixedWidth: root.vertical ? root.barSize : Style.space(20)
|
||||
fixedHeight: root.barSize
|
||||
onPressed: function() { root.focusWorkspace(modelData) }
|
||||
}
|
||||
|
||||
@@ -528,6 +528,8 @@ Panel {
|
||||
bar: root.bar
|
||||
text: root.outputIcon()
|
||||
fontSize: Style.font.body
|
||||
fixedWidth: root.bar && root.bar.vertical ? -1 : Style.space(27)
|
||||
fixedHeight: root.bar && root.bar.vertical ? Style.space(26) : -1
|
||||
onPressed: function(b) {
|
||||
if (b === Qt.RightButton) root.toggleOutputMute()
|
||||
else root.toggle()
|
||||
|
||||
@@ -406,6 +406,8 @@ Panel {
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
text: root.icon
|
||||
fixedWidth: root.bar && root.bar.vertical ? -1 : Style.space(27)
|
||||
fixedHeight: root.bar && root.bar.vertical ? Style.space(26) : -1
|
||||
onPressed: function(b) {
|
||||
if (b === Qt.RightButton) root.toggleBluetooth()
|
||||
else if (b === Qt.MiddleButton) root.bar.run("omarchy-launch-bluetooth")
|
||||
|
||||
@@ -141,7 +141,7 @@ Panel {
|
||||
Item {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
implicitWidth: root.bar && root.bar.vertical ? root.bar.barSize : Style.space(32)
|
||||
implicitWidth: root.bar && root.bar.vertical ? root.bar.barSize : Style.space(27)
|
||||
implicitHeight: root.bar && root.bar.vertical ? Style.space(26) : (root.bar ? root.bar.barSize : Style.space(26))
|
||||
|
||||
property var registeredBar: null
|
||||
|
||||
@@ -343,6 +343,9 @@ Panel {
|
||||
bar: root.bar
|
||||
text: root.displays.length > 1 ? "" : ""
|
||||
fontSize: Style.font.subtitle
|
||||
fixedWidth: root.bar && root.bar.vertical ? -1 : Style.space(27)
|
||||
fixedHeight: root.bar && root.bar.vertical ? Style.space(26) : -1
|
||||
rightExtraMargin: 4
|
||||
onPressed: function(b) { root.toggle() }
|
||||
onWheelMoved: function(delta) {
|
||||
if (root.brightnessAvailable) root.setBrightness(root.brightnessPercent + (delta > 0 ? 5 : -5))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Networking
|
||||
@@ -584,8 +585,9 @@ Panel {
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
text: root.icon
|
||||
horizontalMargin: 8.5
|
||||
rightExtraMargin: 2
|
||||
fixedWidth: root.bar && root.bar.vertical ? -1 : Style.space(27)
|
||||
fixedHeight: root.bar && root.bar.vertical ? Style.space(26) : -1
|
||||
rightExtraMargin: 5.5
|
||||
|
||||
onPressed: function(b) {
|
||||
if (root.opened) root.close()
|
||||
@@ -794,48 +796,41 @@ Panel {
|
||||
width: parent.width
|
||||
spacing: Style.spacing.labelGap
|
||||
|
||||
Row {
|
||||
visible: root.info.rx_bytes !== undefined
|
||||
GridLayout {
|
||||
width: parent.width
|
||||
spacing: Style.space(20)
|
||||
columns: 4
|
||||
columnSpacing: Style.space(20)
|
||||
rowSpacing: Style.spacing.labelGap
|
||||
|
||||
Column {
|
||||
width: (parent.width - parent.spacing) / 2
|
||||
spacing: Style.spacing.labelGap
|
||||
InfoPair { label: "Receiving"; value: root.formatRate(root.downloadRate) }
|
||||
InfoPair { label: "Downloaded"; value: root.formatBytes(parseFloat(root.info.rx_bytes || "0")) }
|
||||
}
|
||||
InfoLabel { visible: root.info.rx_bytes !== undefined; text: "Receiving" }
|
||||
DetailValue { visible: root.info.rx_bytes !== undefined; text: root.formatRate(root.downloadRate) }
|
||||
InfoLabel { visible: root.info.rx_bytes !== undefined; text: "Sending" }
|
||||
DetailValue { visible: root.info.rx_bytes !== undefined; text: root.formatRate(root.uploadRate) }
|
||||
|
||||
Column {
|
||||
width: (parent.width - parent.spacing) / 2
|
||||
spacing: Style.spacing.labelGap
|
||||
InfoPair { label: "Sending"; value: root.formatRate(root.uploadRate) }
|
||||
InfoPair { label: "Uploaded"; value: root.formatBytes(parseFloat(root.info.tx_bytes || "0")) }
|
||||
}
|
||||
}
|
||||
InfoLabel { visible: root.info.rx_bytes !== undefined; text: "Downloaded" }
|
||||
DetailValue { visible: root.info.rx_bytes !== undefined; text: root.formatBytes(parseFloat(root.info.rx_bytes || "0")) }
|
||||
InfoLabel { visible: root.info.rx_bytes !== undefined; text: "Uploaded" }
|
||||
DetailValue { visible: root.info.rx_bytes !== undefined; text: root.formatBytes(parseFloat(root.info.tx_bytes || "0")) }
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Style.space(20)
|
||||
Column {
|
||||
width: (parent.width - parent.spacing) / 2
|
||||
InfoPair {
|
||||
visible: !!root.info.ip
|
||||
label: "IP Address"
|
||||
value: root.info.ip || ""
|
||||
copyable: true
|
||||
tooltipText: "Copy IP"
|
||||
}
|
||||
InfoLabel {
|
||||
visible: !!root.info.ip || !!root.info.gateway
|
||||
text: root.info.ip ? "IP Address" : ""
|
||||
}
|
||||
Column {
|
||||
width: (parent.width - parent.spacing) / 2
|
||||
InfoPair {
|
||||
visible: !!root.info.gateway
|
||||
label: "Gateway"
|
||||
value: root.info.gateway || ""
|
||||
copyable: true
|
||||
tooltipText: "Copy gateway"
|
||||
}
|
||||
DetailValue {
|
||||
visible: !!root.info.ip || !!root.info.gateway
|
||||
text: root.info.ip || ""
|
||||
copyable: !!root.info.ip
|
||||
tooltipText: "Copy IP"
|
||||
}
|
||||
InfoLabel {
|
||||
visible: !!root.info.ip || !!root.info.gateway
|
||||
text: root.info.gateway ? "Gateway" : ""
|
||||
}
|
||||
DetailValue {
|
||||
visible: !!root.info.ip || !!root.info.gateway
|
||||
text: root.info.gateway || ""
|
||||
copyable: !!root.info.gateway
|
||||
tooltipText: "Copy gateway"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1313,35 +1308,26 @@ Panel {
|
||||
onTriggered: if (!networkProc.running) networkProc.running = true
|
||||
}
|
||||
|
||||
component InfoPair: Row {
|
||||
property string label: ""
|
||||
property string value: ""
|
||||
component DetailValue: InfoValue {
|
||||
property bool copyable: false
|
||||
property string tooltipText: "Copy to clipboard"
|
||||
|
||||
width: parent.width
|
||||
spacing: Style.space(8)
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
|
||||
InfoLabel { text: label }
|
||||
Item { width: Math.max(0, parent.width - parent.children[0].implicitWidth - valueText.implicitWidth - parent.spacing * 2); height: 1 }
|
||||
InfoValue {
|
||||
id: valueText
|
||||
text: value
|
||||
MouseArea {
|
||||
id: valueMouse
|
||||
anchors.fill: parent
|
||||
enabled: copyable && parent.text !== ""
|
||||
hoverEnabled: enabled
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: root.copyToClipboard(parent.text)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: valueMouse
|
||||
anchors.fill: parent
|
||||
enabled: copyable && valueText.text !== ""
|
||||
hoverEnabled: enabled
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: root.copyToClipboard(valueText.text)
|
||||
}
|
||||
|
||||
PanelToolTip {
|
||||
visible: valueMouse.enabled && valueMouse.containsMouse
|
||||
text: tooltipText
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
PanelToolTip {
|
||||
visible: valueMouse.enabled && valueMouse.containsMouse
|
||||
text: tooltipText
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ function chargeThresholdActive(device, onBattery, states) {
|
||||
if (!(d && d.isPresent && !onBattery)) return false
|
||||
|
||||
var fraction = batteryFraction(d)
|
||||
if (d.state === s.Discharging || d.state === s.PendingCharge) return true
|
||||
if (d.state === s.Discharging) return false
|
||||
if (d.state === s.PendingCharge) return true
|
||||
if (d.state === s.FullyCharged && fraction < 0.99) return true
|
||||
if (d.state !== s.Charging || fraction >= 0.99) return false
|
||||
|
||||
@@ -74,6 +75,7 @@ function batteryIcon(device, onBattery, states) {
|
||||
if (threshold) return defaultIcons[index]
|
||||
if (d.state === states.FullyCharged) return ""
|
||||
if (d.state === states.Charging) return chargingIcons[index]
|
||||
if (d.state === states.Discharging) return defaultIcons[index]
|
||||
if (!onBattery) return ""
|
||||
return defaultIcons[index]
|
||||
}
|
||||
@@ -85,7 +87,7 @@ function modeLabel(device, onBattery, states) {
|
||||
var percentage = d.isPresent ? d.percentage : 0
|
||||
if (chargeThresholdActive(d, onBattery, states)) return "Threshold"
|
||||
if (!onBattery && percentage >= 1) return "Fully charged"
|
||||
if (onBattery) return "On battery"
|
||||
if (onBattery || d.state === states.Discharging) return "On battery"
|
||||
return "Charging"
|
||||
}
|
||||
|
||||
|
||||
@@ -41,12 +41,12 @@ Panel {
|
||||
|
||||
function batteryIcon() {
|
||||
var device = UPower.displayDevice
|
||||
return Model.batteryIcon(device, UPower.onBattery, upowerStates())
|
||||
return Model.batteryIcon(device, root.discharging, upowerStates())
|
||||
}
|
||||
|
||||
function modeLabel() {
|
||||
var device = UPower.displayDevice
|
||||
return Model.modeLabel(device, UPower.onBattery, upowerStates())
|
||||
return Model.modeLabel(device, root.discharging, upowerStates())
|
||||
}
|
||||
|
||||
function profileIcon(name) {
|
||||
@@ -57,11 +57,15 @@ Panel {
|
||||
var device = UPower.displayDevice
|
||||
return device && device.isPresent && device.state === UPowerDeviceState.FullyCharged && !root.chargeThresholdActive
|
||||
}
|
||||
readonly property bool discharging: {
|
||||
var device = UPower.displayDevice
|
||||
return !!(device && device.isPresent && (UPower.onBattery || device.state === UPowerDeviceState.Discharging))
|
||||
}
|
||||
readonly property bool chargeThresholdActive: {
|
||||
var device = UPower.displayDevice
|
||||
return Model.chargeThresholdActive(device, UPower.onBattery, upowerStates())
|
||||
return Model.chargeThresholdActive(device, root.discharging, upowerStates())
|
||||
}
|
||||
readonly property bool batteryFull: fullyCharged || (!UPower.onBattery && batteryFraction >= 1)
|
||||
readonly property bool batteryFull: fullyCharged || (!root.discharging && batteryFraction >= 1)
|
||||
readonly property bool batteryFlowIdle: batteryFull || chargeThresholdActive
|
||||
|
||||
// 0..1 charge level, used by the visual progress bar.
|
||||
@@ -70,14 +74,12 @@ Panel {
|
||||
return Model.batteryFraction(d)
|
||||
}
|
||||
|
||||
readonly property bool batteryLow: UPower.onBattery && batteryFraction > 0 && batteryFraction <= 0.2
|
||||
readonly property bool charging: {
|
||||
var d = UPower.displayDevice
|
||||
return d && d.isPresent && d.state === UPowerDeviceState.Charging && !root.chargeThresholdActive
|
||||
}
|
||||
|
||||
readonly property color batteryFillColor: {
|
||||
if (batteryLow) return Color.urgent
|
||||
return root.bar ? root.bar.foreground : Color.foreground
|
||||
}
|
||||
|
||||
@@ -111,7 +113,7 @@ Panel {
|
||||
readonly property var activePhrases: {
|
||||
if (fullyCharged) return []
|
||||
if (charging) return chargingPhrases
|
||||
if (UPower.onBattery || chargeThresholdActive) return onBatteryPhrases
|
||||
if (discharging) return onBatteryPhrases
|
||||
return []
|
||||
}
|
||||
readonly property bool rotatingPhrases: activePhrases.length > 0
|
||||
@@ -252,9 +254,8 @@ Panel {
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
text: root.batteryIcon()
|
||||
horizontalMargin: 8.5
|
||||
rightExtraMargin: 2
|
||||
active: root.batteryPresent && UPower.displayDevice.percentage <= 0.2 && UPower.onBattery
|
||||
fixedWidth: root.bar && root.bar.vertical ? -1 : Style.space(27)
|
||||
fixedHeight: root.bar && root.bar.vertical ? Style.space(26) : -1
|
||||
tooltipText: ""
|
||||
onPressed: function(b) { if (root.batteryPresent) root.toggle() }
|
||||
}
|
||||
@@ -296,7 +297,7 @@ Panel {
|
||||
Text {
|
||||
id: heroIcon
|
||||
text: root.batteryIcon()
|
||||
color: root.batteryLow ? Color.urgent : root.bar.foreground
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.display
|
||||
anchors.left: parent.left
|
||||
@@ -340,7 +341,7 @@ Panel {
|
||||
Text {
|
||||
id: heroPercent
|
||||
text: root.batteryInfo.percentage || "—"
|
||||
color: root.batteryLow ? Color.urgent : root.bar.foreground
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.displayLarge
|
||||
font.bold: true
|
||||
@@ -408,17 +409,21 @@ Panel {
|
||||
width: (parent.width - parent.spacing) / 2
|
||||
spacing: Style.spacing.labelGap
|
||||
InfoPair {
|
||||
label: root.chargeThresholdActive ? "Charge limit" : (UPower.onBattery ? "Time left" : "Time to full")
|
||||
label: root.chargeThresholdActive ? "Charge limit" : (root.discharging ? "Time left" : "Time to full")
|
||||
value: root.chargeThresholdActive ? (root.batteryInfo.threshold || "-") : (root.batteryFlowIdle ? "-" : (root.batteryInfo.time || "—"))
|
||||
}
|
||||
InfoPair {
|
||||
label: root.chargeThresholdActive ? "Battery state" : (UPower.onBattery ? "Discharging" : "Charging")
|
||||
label: root.chargeThresholdActive ? "Battery state" : (root.discharging ? "Discharging" : "Charging")
|
||||
value: root.chargeThresholdActive ? "Holding" : (root.batteryFull ? "-" : (root.batteryInfo.rate || ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Power profile picker ----------
|
||||
PanelSeparator {
|
||||
foreground: root.bar.foreground
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Style.space(10)
|
||||
|
||||
@@ -12,6 +12,7 @@ Panel {
|
||||
ipcTarget: "omarchy.tailscale"
|
||||
manageIpc: false
|
||||
|
||||
readonly property real openIndicatorInlineOffset: bar && bar.vertical ? 0 : Style.spaceReal(1.5)
|
||||
property string focusSection: "header"
|
||||
property int headerIndex: 0
|
||||
property int accountIndex: 0
|
||||
@@ -238,7 +239,7 @@ Panel {
|
||||
if (focusSection === "header") {
|
||||
tailscale.toggleTailscale()
|
||||
} else if (focusSection === "auth") {
|
||||
tailscale.authorizeProfileSwitching()
|
||||
tailscale.authorizeTailscaleOperator()
|
||||
} else if (focusSection === "accounts") {
|
||||
var account = selectedAccount()
|
||||
if (account) tailscale.switchAccount(account.id)
|
||||
@@ -362,7 +363,7 @@ Panel {
|
||||
Item {
|
||||
id: button
|
||||
anchors.fill: parent
|
||||
implicitWidth: root.bar && root.bar.vertical ? root.bar.barSize : Style.space(32)
|
||||
implicitWidth: root.bar && root.bar.vertical ? root.bar.barSize : Style.space(27)
|
||||
implicitHeight: root.bar && root.bar.vertical ? Style.space(26) : (root.bar ? root.bar.barSize : Style.space(26))
|
||||
|
||||
property var registeredBar: null
|
||||
@@ -389,6 +390,7 @@ Panel {
|
||||
|
||||
TailscaleIcon {
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: root.openIndicatorInlineOffset
|
||||
anchors.verticalCenterOffset: -Style.space(1)
|
||||
iconSize: Style.space(12) * 0.85
|
||||
color: root.barIconColor
|
||||
@@ -748,7 +750,7 @@ Panel {
|
||||
cursorShape: tailscale.busy ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||
enabled: !tailscale.busy
|
||||
onEntered: root.setAuthCursor()
|
||||
onClicked: tailscale.authorizeProfileSwitching()
|
||||
onClicked: tailscale.authorizeTailscaleOperator()
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -774,7 +776,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "Authorize switching"
|
||||
text: "Authorize Tailscale operator"
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -783,7 +785,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "Allow this user to see and switch Tailscale connections"
|
||||
text: "Allow this user to operate this Tailscale profile"
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -791,14 +793,6 @@ Panel {
|
||||
}
|
||||
}
|
||||
|
||||
PanelActionButton {
|
||||
iconText: ""
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
enabled: !tailscale.busy
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: tailscale.authorizeProfileSwitching()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,14 +988,7 @@ Panel {
|
||||
modal: false
|
||||
focus: true
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
onOpenedChanged: {
|
||||
root.copyMenuOpen = opened
|
||||
if (opened) {
|
||||
peerRow.clampCopyIndex()
|
||||
forceActiveFocus()
|
||||
}
|
||||
}
|
||||
Keys.onPressed: function(event) {
|
||||
function handleKey(event) {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
close()
|
||||
event.accepted = true
|
||||
@@ -1022,6 +1009,15 @@ Panel {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
onOpenedChanged: {
|
||||
root.copyMenuOpen = opened
|
||||
if (opened) {
|
||||
peerRow.clampCopyIndex()
|
||||
Qt.callLater(function() { copyPopupContent.forceActiveFocus() })
|
||||
} else if (root.opened) {
|
||||
Qt.callLater(function() { keyCatcher.forceActiveFocus() })
|
||||
}
|
||||
}
|
||||
background: BorderSurface {
|
||||
color: Color.background
|
||||
borderSpec: Border.flat(root.dim, 1)
|
||||
@@ -1029,7 +1025,11 @@ Panel {
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
id: copyPopupContent
|
||||
width: parent.width
|
||||
focus: true
|
||||
Keys.priority: Keys.BeforeItem
|
||||
Keys.onPressed: function(event) { copyPopup.handleKey(event) }
|
||||
|
||||
Repeater {
|
||||
model: peerRow.copyOptions
|
||||
@@ -1102,6 +1102,7 @@ Panel {
|
||||
readonly property bool activeExitNode: peer && peer.ExitNode === true
|
||||
readonly property bool settingExitNode: peer && tailscale.settingExitNodeId === String(peer.id || "")
|
||||
readonly property string peerName: peer ? String(peer.DisplayName || peer.HostName || "Unknown") : "Unknown"
|
||||
readonly property string actionTooltip: addMullvad ? "" : (activeExitNode ? "Disconnect" : "Connect")
|
||||
|
||||
hasCursor: root.cursorActive && root.focusSection === "exitNodes" && root.exitNodeIndex === rowIndex
|
||||
current: activeExitNode || settingExitNode || (addMullvad && root.mullvadPickerOpen)
|
||||
@@ -1154,12 +1155,19 @@ Panel {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: exitNodeMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: root.setExitNodeCursor(exitNodeRow.rowIndex)
|
||||
onClicked: root.chooseExitNode(exitNodeRow.peer)
|
||||
}
|
||||
|
||||
PanelToolTip {
|
||||
visible: exitNodeRow.actionTooltip !== "" && exitNodeMouse.containsMouse
|
||||
text: exitNodeRow.actionTooltip
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
}
|
||||
|
||||
component MullvadRegionRow: CursorSurface {
|
||||
@@ -1172,6 +1180,7 @@ Panel {
|
||||
readonly property bool activeExitNode: peer && peer.ExitNode === true
|
||||
readonly property bool settingExitNode: peer && tailscale.settingExitNodeId === String(peer.id || "")
|
||||
readonly property bool selectedRegion: root.mullvadPickerOpen && root.mullvadRegionIndex === rowIndex
|
||||
readonly property string actionTooltip: activeExitNode ? "Disconnect" : "Connect"
|
||||
|
||||
foreground: root.foreground
|
||||
fill: root.hoverFill
|
||||
@@ -1226,11 +1235,18 @@ Panel {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: regionMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: root.mullvadRegionIndex = regionRow.rowIndex
|
||||
onClicked: root.chooseExitNode(regionRow.peer)
|
||||
}
|
||||
|
||||
PanelToolTip {
|
||||
visible: regionMouse.containsMouse
|
||||
text: regionRow.actionTooltip
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,4 @@ Renders the Tailscale mark natively as a theme-colored 3×3 dot grid, matching t
|
||||
|
||||
## Add to the bar
|
||||
|
||||
This widget ships as first-party plugin `omarchy.tailscale`. Add it with `omarchy plugin bar add omarchy.tailscale`, or add an entry such as `{ "id": "omarchy.tailscale" }` to one of the `bar.layout` sections in `~/.config/omarchy/shell.json` and restart the shell.
|
||||
This widget ships as first-party plugin `omarchy.tailscale`. Add it with `omarchy plugin bar add omarchy.tailscale`, or add an entry such as `{ "id": "omarchy.tailscale" }` to one of the `bar.layout` sections in `~/.config/omarchy/shell.json`; the shell reloads `shell.json` automatically.
|
||||
|
||||
@@ -291,11 +291,11 @@ Item {
|
||||
exitNodeProcess.running = true
|
||||
}
|
||||
|
||||
function authorizeProfileSwitching() {
|
||||
function authorizeTailscaleOperator() {
|
||||
if (!installed || operatorProcess.running || userName === "") return
|
||||
_operatorOutput = ""
|
||||
_operatorError = ""
|
||||
actionStatus = "Authorizing Tailscale profiles…"
|
||||
actionStatus = "Authorizing Tailscale operator..."
|
||||
operatorProcess.command = ["pkexec", "tailscale", "set", "--operator=" + userName]
|
||||
operatorProcess.running = true
|
||||
}
|
||||
@@ -414,7 +414,7 @@ Item {
|
||||
root.parseAccounts("")
|
||||
if (/profiles access denied/i.test(stderr) || /profiles access denied/i.test(stdout)) {
|
||||
root.accountsAccessDenied = true
|
||||
root.lastError = "Authorize Tailscale profiles to show connections"
|
||||
root.lastError = "Authorize Tailscale operator to show connections"
|
||||
} else {
|
||||
root.lastError = elideStatus(stderr || stdout || "Could not list Tailscale connections")
|
||||
}
|
||||
@@ -534,7 +534,7 @@ Item {
|
||||
} else {
|
||||
root.accountsAccessDenied = false
|
||||
root.lastError = ""
|
||||
root.actionStatus = "Tailscale profiles authorized"
|
||||
root.actionStatus = "Tailscale operator authorized"
|
||||
actionStatusTimer.restart()
|
||||
root._lastAccountsRefreshMs = 0
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ BarWidget {
|
||||
bar: root.bar
|
||||
text: panelLoader.item ? panelLoader.item.label : ""
|
||||
active: panelLoader.item && panelLoader.item.klass === "active"
|
||||
horizontalMargin: 5
|
||||
horizontalMargin: 2.5
|
||||
// Tooltip suppressed because the panel is the detail view.
|
||||
tooltipText: ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user