mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 18:44:45 +00:00
feat(chat): numbered shortcuts on permission sheet (Phase 2.3)
Hermes v2026.4.23's TUI rewrite added 1-9 numbered shortcuts on approval prompts so power users approve/deny without reaching for the mouse. Mirror the pattern in Scarf: Mac PermissionApprovalView: - Each option button gets a "1. ", "2. ", … prefix on its label. - New private View extension `applyingNumberShortcut(index:)` binds the digit `idx + 1` (no modifiers) via .keyboardShortcut. Capped at 9; extra options stay tappable but unbound. iOS PermissionSheet: - Each row gets a monospaced "1." / "2." prefix as a hierarchy hint. - No keyboard binding (phones don't have hardware keyboards), but the numbering matches the Mac pattern so users transitioning between platforms see the same visual structure. Verified: Mac + iOS builds clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1270,7 +1270,12 @@ private struct PermissionSheet: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section("Your response") {
|
Section("Your response") {
|
||||||
ForEach(permission.options, id: \.optionId) { opt in
|
// Visual numbering 1-9 matches the Mac sheet's
|
||||||
|
// keyboard shortcuts; on iPhone the numbers serve
|
||||||
|
// as a hierarchy hint rather than an accelerator
|
||||||
|
// (no hardware keyboard binding). Mirrors the new
|
||||||
|
// Hermes v2026.4.23 TUI pattern.
|
||||||
|
ForEach(Array(permission.options.enumerated()), id: \.element.optionId) { idx, opt in
|
||||||
Button {
|
Button {
|
||||||
Task {
|
Task {
|
||||||
await onRespond(opt.optionId)
|
await onRespond(opt.optionId)
|
||||||
@@ -1278,6 +1283,11 @@ private struct PermissionSheet: View {
|
|||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
|
if idx < 9 {
|
||||||
|
Text("\(idx + 1).")
|
||||||
|
.font(.body.monospaced())
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
Text(opt.name)
|
Text(opt.name)
|
||||||
Spacer()
|
Spacer()
|
||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
|
|||||||
@@ -444,22 +444,34 @@ struct PermissionApprovalView: View {
|
|||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
// Numbered keyboard shortcuts (1–9) on the option buttons.
|
||||||
|
// Mirrors the new TUI pattern Hermes v2026.4.23 ships —
|
||||||
|
// power users approve / deny without reaching for the
|
||||||
|
// mouse. Visible "1." prefixes act as discoverability
|
||||||
|
// hints; the actual key binding goes through
|
||||||
|
// `.keyboardShortcut`. Capped at 9 — extra options stay
|
||||||
|
// tappable but unbound (they'd need modifiers to
|
||||||
|
// disambiguate beyond 9, which isn't worth it).
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
ForEach(options, id: \.optionId) { option in
|
ForEach(Array(options.enumerated()), id: \.element.optionId) { idx, option in
|
||||||
|
let label = idx < 9 ? "\(idx + 1). \(option.name)" : option.name
|
||||||
|
Group {
|
||||||
if option.optionId == "deny" {
|
if option.optionId == "deny" {
|
||||||
Button(option.name) {
|
Button(label) {
|
||||||
onRespond(option.optionId)
|
onRespond(option.optionId)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
} else {
|
} else {
|
||||||
Button(option.name) {
|
Button(label) {
|
||||||
onRespond(option.optionId)
|
onRespond(option.optionId)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.applyingNumberShortcut(index: idx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(24)
|
.padding(24)
|
||||||
@@ -484,3 +496,17 @@ struct PermissionApprovalView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension View {
|
||||||
|
/// Bind the digit `idx + 1` (1-9) to this view as a no-modifier
|
||||||
|
/// keyboard shortcut. Indices ≥ 9 silently skip — there are only
|
||||||
|
/// nine numeric shortcut keys without modifier conflicts.
|
||||||
|
@ViewBuilder
|
||||||
|
func applyingNumberShortcut(index idx: Int) -> some View {
|
||||||
|
if idx < 9, let scalar = Unicode.Scalar(48 + idx + 1) {
|
||||||
|
self.keyboardShortcut(KeyEquivalent(Character(scalar)), modifiers: [])
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user