From 3435327bc074a7ba8c3fe8939c97de54bbdefd65 Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Thu, 22 Jan 2026 05:10:53 -0600 Subject: [PATCH] fix(app): session screen accessibility improvements (#9907) --- .../components/dialog-connect-provider.tsx | 2 +- .../src/components/dialog-edit-project.tsx | 2 + .../src/components/dialog-select-model.tsx | 14 +++-- .../src/components/dialog-select-server.tsx | 1 + packages/app/src/components/prompt-input.tsx | 50 ++++++++++++----- .../src/components/session-context-usage.tsx | 2 +- .../src/components/session/session-header.tsx | 55 ++++++++++--------- .../session/session-sortable-tab.tsx | 2 +- .../session/session-sortable-terminal-tab.tsx | 1 + packages/app/src/pages/layout.tsx | 11 ++-- packages/app/src/pages/session.tsx | 15 +++-- packages/ui/src/components/dialog.tsx | 2 +- packages/ui/src/components/image-preview.tsx | 2 +- packages/ui/src/components/list.tsx | 2 +- packages/ui/src/components/message-part.tsx | 1 + packages/ui/src/components/popover.tsx | 16 +++--- packages/ui/src/components/session-turn.tsx | 14 +++-- packages/ui/src/components/text-field.tsx | 1 + packages/ui/src/components/toast.tsx | 2 +- 19 files changed, 120 insertions(+), 75 deletions(-) diff --git a/packages/app/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx index 4ec4c8daa..d8d4ad9c2 100644 --- a/packages/app/src/components/dialog-connect-provider.tsx +++ b/packages/app/src/components/dialog-connect-provider.tsx @@ -143,7 +143,7 @@ export function DialogConnectProvider(props: { provider: string }) { } return ( - }> + }>
diff --git a/packages/app/src/components/dialog-edit-project.tsx b/packages/app/src/components/dialog-edit-project.tsx index acf146ef5..cf5535a67 100644 --- a/packages/app/src/components/dialog-edit-project.tsx +++ b/packages/app/src/components/dialog-edit-project.tsx @@ -193,6 +193,8 @@ export function DialogEditProject(props: { project: LocalProject }) { {(color) => (
)} @@ -1524,6 +1525,7 @@ export const PromptInput: Component = (props) => { variant="ghost" class="h-6 w-6" onClick={() => prompt.context.remove(item.key)} + aria-label="Remove file from context" />
)} @@ -1556,6 +1558,7 @@ export const PromptInput: Component = (props) => { type="button" onClick={() => removeImageAttachment(attachment.id)} class="absolute -top-1.5 -right-1.5 size-5 rounded-full bg-surface-raised-stronger-non-alpha border border-border-base flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity hover:bg-surface-raised-base-hover" + aria-label="Remove attachment" > @@ -1574,6 +1577,13 @@ export const PromptInput: Component = (props) => { editorRef = el props.ref?.(el) }} + role="textbox" + aria-multiline="true" + aria-label={ + store.mode === "shell" + ? language.t("prompt.placeholder.shell") + : language.t("prompt.placeholder.normal", { example: language.t(EXAMPLES[store.placeholder]) }) + } contenteditable="true" onInput={handleInput} onPaste={handlePaste} @@ -1638,21 +1648,22 @@ export const PromptInput: Component = (props) => { } > - - + - - - + + + + {local.model.current()?.name ?? language.t("dialog.model.select.title")} + + + 0}> = (props) => { "text-text-base": !permission.isAutoAccepting(params.id!, sdk.directory), "hover:bg-surface-success-base": permission.isAutoAccepting(params.id!, sdk.directory), }} + aria-label="Toggle auto-accept permissions" + aria-pressed={permission.isAutoAccepting(params.id!, sdk.directory)} > = (props) => { - @@ -1743,6 +1762,7 @@ export const PromptInput: Component = (props) => { icon={working() ? "stop" : "arrow-up"} variant="primary" class="h-6 w-4.5" + aria-label={working() ? "Stop" : "Send message"} /> diff --git a/packages/app/src/components/session-context-usage.tsx b/packages/app/src/components/session-context-usage.tsx index 53148d416..677e29dbb 100644 --- a/packages/app/src/components/session-context-usage.tsx +++ b/packages/app/src/components/session-context-usage.tsx @@ -96,7 +96,7 @@ export function SessionContextUsage(props: SessionContextUsageProps) { {circle()} - diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 98b9635c5..f2ffa3ec5 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -135,6 +135,7 @@ export function SessionHeader() { type="button" class="hidden md:flex w-[320px] p-1 pl-1.5 items-center gap-2 justify-between rounded-md border border-border-weak-base bg-surface-raised-base transition-colors cursor-default hover:bg-surface-raised-base-hover focus:bg-surface-raised-base-hover active:bg-surface-raised-base-active" onClick={() => command.trigger("file.open")} + aria-label="Search files" >
@@ -184,6 +185,10 @@ export function SessionHeader() { variant="ghost" class="group/review-toggle size-6 p-0" onClick={() => view().reviewPanel.toggle()} + aria-label="Toggle review panel" + aria-expanded={view().reviewPanel.opened()} + aria-controls="review-panel" + tabIndex={showReview() ? 0 : -1} >
view().terminal.toggle()} + aria-label="Toggle terminal" + aria-expanded={view().terminal.opened()} + aria-controls="terminal-panel" >
-
- - - - } - > + +
+
-
+
+
)} diff --git a/packages/app/src/components/session/session-sortable-tab.tsx b/packages/app/src/components/session/session-sortable-tab.tsx index a4a434b05..6f2c044fc 100644 --- a/packages/app/src/components/session/session-sortable-tab.tsx +++ b/packages/app/src/components/session/session-sortable-tab.tsx @@ -37,7 +37,7 @@ export function SortableTab(props: { tab: string; onTabClose: (tab: string) => v value={props.tab} closeButton={ - props.onTabClose(props.tab)} /> + props.onTabClose(props.tab)} aria-label="Close tab" /> } hideCloseButton diff --git a/packages/app/src/components/session/session-sortable-terminal-tab.tsx b/packages/app/src/components/session/session-sortable-terminal-tab.tsx index a3aaf6061..3ef395c9a 100644 --- a/packages/app/src/components/session/session-sortable-terminal-tab.tsx +++ b/packages/app/src/components/session/session-sortable-terminal-tab.tsx @@ -139,6 +139,7 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () => e.stopPropagation() close() }} + aria-label="Close terminal" /> } > diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index befdf721d..2a050543c 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -1916,6 +1916,7 @@ export default function Layout(props: ParentProps) { "bg-surface-base-hover border border-border-weak-base": !selected() && open(), }} onClick={() => navigateToProject(props.project.worktree)} + onBlur={() => setOpen(false)} > @@ -2343,7 +2344,8 @@ export default function Layout(props: ParentProps) {
-
-
+
-
e.stopPropagation()} > -
+
-
+
@@ -1913,12 +1915,15 @@ export default function Page() { -
+
@@ -1990,7 +1995,7 @@ export default function Page() { keybind={command.keybind("terminal.new")} class="flex items-center" > - +
diff --git a/packages/ui/src/components/dialog.tsx b/packages/ui/src/components/dialog.tsx index 4b871732d..cff9edaf2 100644 --- a/packages/ui/src/components/dialog.tsx +++ b/packages/ui/src/components/dialog.tsx @@ -40,7 +40,7 @@ export function Dialog(props: DialogProps) { {props.action} - +
diff --git a/packages/ui/src/components/image-preview.tsx b/packages/ui/src/components/image-preview.tsx index 88bf38980..1ab868576 100644 --- a/packages/ui/src/components/image-preview.tsx +++ b/packages/ui/src/components/image-preview.tsx @@ -14,7 +14,7 @@ export function ImagePreview(props: ImagePreviewProps) {
- +
{props.alt diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx index aeb2769d6..41e528e79 100644 --- a/packages/ui/src/components/list.tsx +++ b/packages/ui/src/components/list.tsx @@ -230,7 +230,7 @@ export function List(props: ListProps & { ref?: (ref: ListRef) => void }) />
- setInternalFilter("")} /> + setInternalFilter("")} aria-label="Clear filter" />
{searchAction()} diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index d1788f1df..d639c5224 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -429,6 +429,7 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp event.stopPropagation() handleCopy() }} + aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")} />
diff --git a/packages/ui/src/components/popover.tsx b/packages/ui/src/components/popover.tsx index 3262098e5..3de7cb516 100644 --- a/packages/ui/src/components/popover.tsx +++ b/packages/ui/src/components/popover.tsx @@ -1,21 +1,23 @@ import { Popover as Kobalte } from "@kobalte/core/popover" -import { ComponentProps, JSXElement, ParentProps, Show, splitProps } from "solid-js" +import { ComponentProps, JSXElement, ParentProps, Show, splitProps, ValidComponent } from "solid-js" import { IconButton } from "./icon-button" -export interface PopoverProps extends ParentProps, Omit, "children"> { - trigger: JSXElement +export interface PopoverProps extends ParentProps, Omit, "children"> { + trigger?: JSXElement + triggerAs?: T + triggerProps?: ComponentProps title?: JSXElement description?: JSXElement class?: ComponentProps<"div">["class"] classList?: ComponentProps<"div">["classList"] } -export function Popover(props: PopoverProps) { - const [local, rest] = splitProps(props, ["trigger", "title", "description", "class", "classList", "children"]) +export function Popover(props: PopoverProps) { + const [local, rest] = splitProps(props, ["trigger", "triggerAs", "triggerProps", "title", "description", "class", "classList", "children"]) return ( - + {local.trigger} @@ -30,7 +32,7 @@ export function Popover(props: PopoverProps) {
{local.title} - +
diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index 06e421882..1cd210499 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -506,13 +506,13 @@ export function SessionTurn( 0}> -
+
{/* User Message */} -
+
@@ -525,6 +525,7 @@ export function SessionTurn( variant="ghost" size="small" onClick={props.onStepsExpandedToggle ?? (() => {})} + aria-expanded={props.stepsExpanded} > @@ -552,8 +553,8 @@ export function SessionTurn( {i18n.t("ui.sessionTurn.steps.hide")} {i18n.t("ui.sessionTurn.steps.show")} - · - {store.duration} + + {store.duration} 0}> @@ -563,7 +564,7 @@ export function SessionTurn(
{/* Response */} 0}> -
+
{(assistantMessage) => ( {/* Response */} +
+ {!working() && response() ? response() : ""} +
diff --git a/packages/ui/src/components/text-field.tsx b/packages/ui/src/components/text-field.tsx index a4eafa561..0694e421e 100644 --- a/packages/ui/src/components/text-field.tsx +++ b/packages/ui/src/components/text-field.tsx @@ -103,6 +103,7 @@ export function TextField(props: TextFieldProps) { variant="ghost" onClick={handleCopy} data-slot="input-copy-button" + aria-label={copied() ? i18n.t("ui.textField.copied") : i18n.t("ui.textField.copyToClipboard")} /> diff --git a/packages/ui/src/components/toast.tsx b/packages/ui/src/components/toast.tsx index f34c46d42..9493e50ca 100644 --- a/packages/ui/src/components/toast.tsx +++ b/packages/ui/src/components/toast.tsx @@ -62,7 +62,7 @@ function ToastActions(props: ComponentProps<"div">) { } function ToastCloseButton(props: ToastCloseButtonProps & ComponentProps<"button">) { - return + return } function ToastProgressTrack(props: ComponentProps) {