feat(quickshell): give the prompt its own panel, ESC handling and placement

PolkitPanel replaces BarPanel as the dialog's chrome. Deliberately not a
subclass or a fork: most of BarPanel is density machinery — summary slot,
animated height, state pair, transitions — that a modal never uses, and
inheriting it would tie the dialog's look to a component whose real job is the
rail, so every restyle here would have to be justified against the panels up
there. It keeps the shell's silhouette (cut corners with detached accent caps,
the accent rules, the header strip) and drops the rail's tick decoration.

ESC closes the rail. The bar had no keyboard focus at all, so this adds it,
gated by the shell rather than left to the compositor to arbitrate between two
exclusive surfaces — that resolves by stacking and would invert silently the
day the layers change:

    grabsKeyboard: shell.expanded && !polkit.prompting

so ESC dismisses the prompt while one is open and closes the rail afterwards.
Verified by instrumenting the handoff: expanded -> true, prompt open -> false,
prompt dismissed -> true, collapsed -> false. Note the rail now takes EXCLUSIVE
keyboard focus while expanded, which is the cost of answering a keypress the
user has not aimed at anything.

The dialog sits a third of the way down rather than centred, panel centre on
the third so it grows symmetrically as the message wraps, floored at a margin
so a tall prompt on a short output cannot be pushed off the top.

Also carries the backdrop tuning: dim 0.75 -> 0.65, gridOpacity 0.15 -> 0.10,
crossOpacity 0.45 -> 0.15, now that the scrim is used by the prompt as well as
the rail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GAq2kKCLazZmrKvkd3akud
This commit is contained in:
2026-09-01 23:38:23 +02:00
co-authored by Claude Opus 5
parent 27b924f5e9
commit b3c3cc38f0
6 changed files with 273 additions and 22 deletions
@@ -5,7 +5,6 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import qs.HyprChrome.Theme
import qs.HyprChrome.Widgets.Bar.Panels
// Headlessly renderable visual core of the polkit authentication prompt.
//
@@ -73,7 +72,7 @@ Item {
elide: Text.ElideRight
}
BarPanel {
PolkitPanel {
id: panel
width: root.width
@@ -82,23 +81,10 @@ Item {
// The action id is the one piece that says WHAT is being authorized
// independently of the (localizable, often vague) message.
meta: root.actionId
chamfer: 16
// A modal, not a rail panel: clicking the body must reach the input
// rather than collapse the dialog out from under it.
expanded: true
toggleOnClick: false
// Collapsed rendering is never shown here, but BarPanel keeps both
// slots instantiated, so the summary stays bound to the same truth.
summary: MicroText {
text: root.inputPrompt
color: Theme.text
}
// Sized like every other BarPanel body: the slot decides the width and
// the layout's implicitHeight becomes its height, so a wrapped message
// or an extra pam_info line grows the panel instead of being clipped.
// The body slot decides the width and the layout's implicitHeight
// becomes its height, so a wrapped message or an extra pam_info line
// grows the panel instead of being clipped.
ColumnLayout {
width: parent.width
spacing: 10