fix(ui): allow out-of-order permission clicks

Show permission action buttons for queued tool calls while keeping keyboard shortcuts bound to the first active request. Prevent permission center list clicks from overriding keyboard-active ordering.
This commit is contained in:
Shantur Rathore
2026-01-21 13:26:37 +00:00
parent 0bf22a323f
commit 6e9c5a88b4
2 changed files with 42 additions and 53 deletions

View File

@@ -7,8 +7,6 @@ import {
getPermissionQueue, getPermissionQueue,
getQuestionQueue, getQuestionQueue,
getQuestionEnqueuedAtForInstance, getQuestionEnqueuedAtForInstance,
setActivePermissionIdForInstance,
setActiveQuestionIdForInstance,
} from "../stores/instances" } from "../stores/instances"
import { ensureSessionParentExpanded, loadMessages, sessions as sessionStateSessions, setActiveSessionFromList } from "../stores/sessions" import { ensureSessionParentExpanded, loadMessages, sessions as sessionStateSessions, setActiveSessionFromList } from "../stores/sessions"
import { messageStoreBus } from "../stores/message-v2/bus" import { messageStoreBus } from "../stores/message-v2/bus"
@@ -265,19 +263,10 @@ const PermissionApprovalModal: Component<PermissionApprovalModalProps> = (props)
return count === 1 ? "1 question" : `${count} questions` return count === 1 ? "1 question" : `${count} questions`
} }
const handleActivate = () => {
if (item.kind === "permission") {
setActivePermissionIdForInstance(props.instanceId, item.id)
} else {
setActiveQuestionIdForInstance(props.instanceId, item.id)
}
}
return ( return (
<div <div
class={`permission-center-item${isActive() ? " permission-center-item-active" : ""}`} class={`permission-center-item${isActive() ? " permission-center-item-active" : ""}`}
role="listitem" role="listitem"
onClick={handleActivate}
> >
<div class="permission-center-item-header"> <div class="permission-center-item-header">
<div class="permission-center-item-heading"> <div class="permission-center-item-heading">

View File

@@ -7,6 +7,7 @@ import { useGlobalCache } from "../lib/hooks/use-global-cache"
import { useConfig } from "../stores/preferences" import { useConfig } from "../stores/preferences"
import type { DiffViewMode } from "../stores/preferences" import type { DiffViewMode } from "../stores/preferences"
import { activeInterruption, sendPermissionResponse, sendQuestionReject, sendQuestionReply } from "../stores/instances" import { activeInterruption, sendPermissionResponse, sendQuestionReject, sendQuestionReply } from "../stores/instances"
import type { PermissionRequestLike } from "../types/permission"
import { getPermissionDisplayTitle, getPermissionKind, getPermissionSessionId } from "../types/permission" import { getPermissionDisplayTitle, getPermissionKind, getPermissionSessionId } from "../types/permission"
import type { QuestionRequest } from "@opencode-ai/sdk/v2" import type { QuestionRequest } from "@opencode-ai/sdk/v2"
import type { TextPart, RenderCache } from "../types/message" import type { TextPart, RenderCache } from "../types/message"
@@ -859,15 +860,17 @@ export default function ToolCall(props: ToolCallProps) {
const activeKey = activePermissionKey() const activeKey = activePermissionKey()
if (!activeKey) return if (!activeKey) return
const handler = (event: KeyboardEvent) => { const handler = (event: KeyboardEvent) => {
const permission = permissionDetails()
if (!permission || !isPermissionActive()) return
if (event.key === "Enter") { if (event.key === "Enter") {
event.preventDefault() event.preventDefault()
handlePermissionResponse("once") void handlePermissionResponse(permission, "once")
} else if (event.key === "a" || event.key === "A") { } else if (event.key === "a" || event.key === "A") {
event.preventDefault() event.preventDefault()
handlePermissionResponse("always") void handlePermissionResponse(permission, "always")
} else if (event.key === "d" || event.key === "D") { } else if (event.key === "d" || event.key === "D") {
event.preventDefault() event.preventDefault()
handlePermissionResponse("reject") void handlePermissionResponse(permission, "reject")
} }
} }
document.addEventListener("keydown", handler) document.addEventListener("keydown", handler)
@@ -1240,11 +1243,8 @@ export default function ToolCall(props: ToolCallProps) {
return renderer().renderBody(rendererContext) return renderer().renderBody(rendererContext)
} }
async function handlePermissionResponse(response: "once" | "always" | "reject") { async function handlePermissionResponse(permission: PermissionRequestLike, response: "once" | "always" | "reject") {
const permission = permissionDetails() if (!permission) return
if (!permission || !isPermissionActive()) {
return
}
setPermissionSubmitting(true) setPermissionSubmitting(true)
setPermissionError(null) setPermissionError(null)
try { try {
@@ -1310,37 +1310,37 @@ export default function ToolCall(props: ToolCallProps) {
</div> </div>
)} )}
</Show> </Show>
<Show <Show when={!active}>
when={active} <p class="tool-call-permission-queued-text">Waiting for earlier permission responses.</p>
fallback={<p class="tool-call-permission-queued-text">Waiting for earlier permission responses.</p>} </Show>
> <div class="tool-call-permission-actions">
<div class="tool-call-permission-actions"> <div class="tool-call-permission-buttons">
<div class="tool-call-permission-buttons"> <button
<button type="button"
type="button" class="tool-call-permission-button"
class="tool-call-permission-button" disabled={permissionSubmitting()}
disabled={permissionSubmitting()} onClick={() => void handlePermissionResponse(permission, "once")}
onClick={() => handlePermissionResponse("once")} >
> Allow Once
Allow Once </button>
</button> <button
<button type="button"
type="button" class="tool-call-permission-button"
class="tool-call-permission-button" disabled={permissionSubmitting()}
disabled={permissionSubmitting()} onClick={() => void handlePermissionResponse(permission, "always")}
onClick={() => handlePermissionResponse("always")} >
> Always Allow
Always Allow </button>
</button> <button
<button type="button"
type="button" class="tool-call-permission-button"
class="tool-call-permission-button" disabled={permissionSubmitting()}
disabled={permissionSubmitting()} onClick={() => void handlePermissionResponse(permission, "reject")}
onClick={() => handlePermissionResponse("reject")} >
> Deny
Deny </button>
</button> </div>
</div> <Show when={active}>
<div class="tool-call-permission-shortcuts"> <div class="tool-call-permission-shortcuts">
<kbd class="kbd">Enter</kbd> <kbd class="kbd">Enter</kbd>
<span>Allow once</span> <span>Allow once</span>
@@ -1349,10 +1349,10 @@ export default function ToolCall(props: ToolCallProps) {
<kbd class="kbd">D</kbd> <kbd class="kbd">D</kbd>
<span>Deny</span> <span>Deny</span>
</div> </div>
</div>
<Show when={permissionError()}>
<div class="tool-call-permission-error">{permissionError()}</div>
</Show> </Show>
</div>
<Show when={permissionError()}>
<div class="tool-call-permission-error">{permissionError()}</div>
</Show> </Show>
</div> </div>
</div> </div>