Files
omarchy/plugins/io.github.elevate08.qs-bitwarden-cli/SshUnlockScreen.qml
T
asepharyana 1cdb82a76f Sync config from arch
- hypr/apps.lua
- hypr/autostart.lua
- hypr/envs.lua
- hypr/hyprland.lua
- hypr/hyprsunset.conf
- hypr/input.lua
- hypr/looknfeel.lua
- hypr/omasettings.lua
- hypr/xdph.conf
- omarchy/branding/about.txt
- omarchy/branding/screensaver.txt
- omarchy/extensions/omarchy-menu.jsonc
- omarchy/hooks/battery-low.d/play-warning-sound.sample
- omarchy/hooks/font-set.d/show-font-notification.sample
- omarchy/hooks/post-boot.d/weather.sample
- omarchy/hooks/post-update.d/install-voxtype.hook
- omarchy/hooks/post-update.d/setup-agent.hook
- omarchy/hooks/post-update.d/setup-fingerprint.hook
- omarchy/hooks/post-update.d/show-update-notification.sample
- omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample
- omarchy/hooks/theme-set.d/show-theme-notification.sample
- omarchy/shell.json
- omarchy/shell.toml
- omarchy/theme.name
- omarchy/themes/azure-glow/README.md
- omarchy/themes/azure-glow/alacritty.toml
- omarchy/themes/azure-glow/btop.theme
- omarchy/themes/azure-glow/hyprland.conf
- omarchy/themes/azure-glow/hyprlock.conf
- omarchy/themes/azure-glow/icons.theme
- … 269 more
2026-09-23 15:19:12 +07:00

333 lines
10 KiB
QML

import QtQuick
import qs.Commons
import qs.Ui
import "BitwardenModel.js" as Model
// The first step of an SSH request when the vault is locked. Uses the same
// layout, pulsing fingerprint animation, and unlock controls as the panel's
// unlock screen, while keeping the SSH request context visible.
Column {
id: screen
required property var panel
property bool active: false
visible: active && panel.sshUnlockRequest !== null
width: parent ? parent.width : 0
spacing: Style.space(12)
onVisibleChanged: if (!visible && eyeBtnUnlock) eyeBtnUnlock.revealed = false
onActiveChanged: if (!active && eyeBtnUnlock) eyeBtnUnlock.revealed = false
component UnlockCaption: Text {
textFormat: Text.PlainText
width: parent ? parent.width : 0
color: screen.panel.dim
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.caption
wrapMode: Text.WordWrap
}
function focusDefault() {
if (!screen.active || !screen.visible) return
screen.panel.prepareUnlock()
if (screen.panel.fingerprintReady) screen.panel.startFingerprintUnlock()
Qt.callLater(function() {
if (!screen.active || screen.panel.status !== "locked") return
if (screen.panel.pinReady) pinField.forceActiveFocus()
else passwordField.forceActiveFocus()
})
}
// Centered header matching Panel.qml Screen 2
Column {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Style.space(6)
Text {
id: fingerprintIcon
textFormat: Text.PlainText
anchors.horizontalCenter: parent.horizontalCenter
text: screen.panel.fingerprintScanning ? "󰈷" : "󰌋"
color: screen.panel.fingerprintScanning ? Color.accent : screen.panel.fg
opacity: 0.85
font.family: screen.panel.fontFamily
font.pixelSize: Style.space(38)
SequentialAnimation on opacity {
running: screen.panel.fingerprintScanning
loops: Animation.Infinite
NumberAnimation { to: 0.35; duration: 700; easing.type: Easing.InOutQuad }
NumberAnimation { to: 0.95; duration: 700; easing.type: Easing.InOutQuad }
onStopped: fingerprintIcon.opacity = 0.85
}
}
Text {
textFormat: Text.PlainText
anchors.horizontalCenter: parent.horizontalCenter
text: screen.panel.status === "unlocked"
? "Loading SSH keys"
: (screen.panel.fingerprintReady ? "Unlock Vault" : "Enter Master Password")
color: screen.panel.fg
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.title
font.bold: true
}
Text {
textFormat: Text.PlainText
visible: screen.panel.userEmail !== ""
anchors.horizontalCenter: parent.horizontalCenter
text: screen.panel.userEmail
color: screen.panel.dim
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
}
UnlockCaption {
text: {
var request = screen.panel.sshUnlockRequest
var prefix = "Vault needs to be unlocked first: "
if (!request) return "Vault needs to be unlocked first."
if (request.keyName !== "") {
return prefix + request.keyName + " is needed by " + request.processName + "."
}
return prefix + request.processName + " is asking which SSH keys are available."
}
horizontalAlignment: Text.AlignHCenter
color: screen.panel.fg
}
UnlockCaption {
text: "Unlocking only loads the key. You will still approve the signing request separately."
horizontalAlignment: Text.AlignHCenter
}
// Fingerprint status / prompt
Text {
textFormat: Text.PlainText
visible: screen.panel.fingerprintMessage !== ""
width: parent.width
horizontalAlignment: Text.AlignHCenter
text: screen.panel.fingerprintMessage
color: screen.panel.fingerprintScanning ? Color.accent : screen.panel.dim
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.bodySmall
wrapMode: Text.WordWrap
}
// Offered when fingerprint unlock is on but nothing is stored yet
Text {
textFormat: Text.PlainText
visible: screen.panel.fingerprintUnlock && screen.panel.fingerprintAvailable && !screen.panel.fingerprintStored
width: parent.width
horizontalAlignment: Text.AlignHCenter
text: "󰈷 Unlock once with your master password to enable fingerprint unlock."
color: screen.panel.dim
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.caption
wrapMode: Text.WordWrap
}
// Checking / keys loading into helper indicator
Rectangle {
visible: screen.panel.status === "checking"
|| (screen.panel.status === "unlocked" && screen.panel.sshAgentLoadActive)
width: parent.width
height: loadingText.implicitHeight + Style.space(20)
radius: Style.cornerRadius
color: Util.alpha(Color.popups.text, 0.06)
Text {
id: loadingText
textFormat: Text.PlainText
anchors.centerIn: parent
width: parent.width - Style.space(24)
text: screen.panel.status === "checking"
? "Checking vault status..."
: Model.sshAgentLoadingNote()
color: screen.panel.fg
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.bodySmall
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
}
// PIN entry, offered above the password field when one is set
Column {
visible: screen.panel.status === "locked" && screen.panel.pinReady
width: parent.width
spacing: Style.space(8)
Text {
textFormat: Text.PlainText
text: "PIN"
color: screen.panel.dim
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.caption
font.bold: true
}
Row {
width: parent.width
spacing: Style.space(8)
TextField {
id: pinField
width: parent.width - pinUnlockBtn.width - Style.space(8)
placeholderText: "Enter your PIN..."
password: true
text: screen.panel.pinEntry
onTextChanged: screen.panel.pinEntry = text.replace(/[^0-9]/g, "")
onAccepted: screen.panel.submitPinUnlock()
enabled: !screen.panel.pinBusy && !screen.panel.isUnlocking
}
Button {
id: pinUnlockBtn
text: screen.panel.pinBusy ? "Checking..." : "Unlock"
iconText: screen.panel.pinBusy ? "󰑐" : "󰌿"
iconSpinning: screen.panel.pinBusy
selected: true
accent: Color.accent
fontFamily: screen.panel.fontFamily
focusable: true
enabled: !screen.panel.pinBusy && !screen.panel.isUnlocking
onClicked: screen.panel.submitPinUnlock()
}
}
Text {
textFormat: Text.PlainText
visible: screen.panel.pinError !== ""
width: parent.width
text: screen.panel.pinError
color: screen.panel.urgent
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.bodySmall
wrapMode: Text.WordWrap
}
Text {
textFormat: Text.PlainText
text: "or use your master password below"
color: screen.panel.dim
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.caption
}
}
// Fingerprint / Password column matching Panel.qml
Column {
visible: screen.panel.status === "locked"
width: parent.width
spacing: Style.space(10)
Button {
visible: screen.panel.fingerprintReady
width: parent.width
text: screen.panel.fingerprintScanning ? "Waiting for fingerprint..." : "Unlock with Fingerprint"
iconText: "󰈷"
selected: true
accent: Color.accent
fontFamily: screen.panel.fontFamily
focusable: true
enabled: !screen.panel.isUnlocking && !screen.panel.fingerprintScanning
onClicked: screen.panel.startFingerprintUnlock()
}
Row {
width: parent.width
spacing: Style.space(8)
TextField {
id: passwordField
width: parent.width - eyeBtnUnlock.width - Style.space(8)
placeholderText: "Master password..."
password: !eyeBtnUnlock.revealed
text: screen.panel.masterPassword
onTextChanged: screen.panel.masterPassword = text
onActiveFocusChanged: if (activeFocus) screen.panel.prepareUnlock()
onAccepted: screen.panel.unlockVault()
enabled: !screen.panel.isUnlocking
}
Button {
id: eyeBtnUnlock
property bool revealed: false
iconText: revealed ? "󰈉" : "󰈈"
tooltipText: revealed ? "Hide password" : "Show password"
fontFamily: screen.panel.fontFamily
focusable: true
onClicked: revealed = !revealed
}
}
Button {
width: parent.width
text: screen.panel.isUnlocking ? "Unlocking..." : "Unlock Vault"
iconText: screen.panel.isUnlocking ? "󰑐" : "󰌋"
iconSpinning: screen.panel.isUnlocking
selected: true
accent: Color.accent
fontFamily: screen.panel.fontFamily
focusable: true
enabled: !screen.panel.isUnlocking
onClicked: screen.panel.unlockVault()
}
}
UnlockCaption {
visible: screen.panel.errorMessage !== ""
text: screen.panel.errorMessage
color: screen.panel.urgent
horizontalAlignment: Text.AlignHCenter
}
UnlockCaption {
visible: screen.panel.status === "unauthenticated"
text: "Sign in from the Bitwarden panel before using vault SSH keys."
color: screen.panel.urgent
horizontalAlignment: Text.AlignHCenter
}
Row {
width: parent.width
spacing: Style.space(8)
Button {
text: "Not now (Esc)"
iconText: "󰅘"
fontFamily: screen.panel.fontFamily
fontSize: Style.font.bodySmall
focusable: true
onClicked: screen.panel.denySshRequest()
}
Button {
visible: screen.panel.sshUnlockPendingCount > 1
text: "Deny all (" + screen.panel.sshUnlockPendingCount + ")"
iconText: "󰅙"
fontFamily: screen.panel.fontFamily
fontSize: Style.font.bodySmall
focusable: true
onClicked: screen.panel.denyAllSshRequests()
}
Item { width: Math.max(0, parent.width - Style.space(screen.panel.sshUnlockPendingCount > 1 ? 280 : 160)); height: 1 }
Text {
textFormat: Text.PlainText
anchors.verticalCenter: parent.verticalCenter
text: screen.panel.sshPromptRemainingSec + "s left"
color: screen.panel.sshPromptRemainingSec <= 5
? screen.panel.urgent : screen.panel.dim
font.family: screen.panel.fontFamily
font.pixelSize: Style.font.caption
}
}
}