mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-26 01:34:51 +08:00
fix(a11y): add accessible names to GUI controls (#7049)
Give icon-only back buttons, the header local-routing switch, the project switcher popover, the Claude JSON editors, and the existing-Skills import checkboxes programmatic names so screen readers announce their purpose. Reuses existing i18n strings where possible and adds two short localized labels across zh/en/ja/zh-TW. Fixes #7048
This commit is contained in:
1 parent
460aa8c73d
commit
c58a25b2ae
11 files changed
+26
-1
No files matched your search
@@ -1283,6 +1283,7 @@ function App() {
|
||||
variant="outline"
|
||||
size="icon"
|
||||
disabled={managementBusy}
|
||||
aria-label={t("common.back")}
|
||||
onClick={() =>
|
||||
setCurrentView(
|
||||
currentView === "skillsDiscovery"
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { motion, AnimatePresence, useReducedMotion } from "framer-motion";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
isWindows,
|
||||
@@ -63,6 +64,7 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
|
||||
contentClassName,
|
||||
motionPreset = "fade",
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const prefersReducedMotion = useReducedMotion();
|
||||
const shouldSlideFromRight =
|
||||
motionPreset === "slide-from-right" && !prefersReducedMotion;
|
||||
@@ -164,6 +166,7 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={onClose}
|
||||
aria-label={t("common.back")}
|
||||
className="rounded-lg select-none"
|
||||
style={{ WebkitAppRegion: "no-drag" } as React.CSSProperties}
|
||||
>
|
||||
|
||||
@@ -110,6 +110,9 @@ export function ProfileSwitcher({ activeApp }: ProfileSwitcherProps) {
|
||||
type="button"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
aria-label={t("profiles.switcherAriaLabel", {
|
||||
name: currentProfile?.name ?? t("profiles.none"),
|
||||
})}
|
||||
title={t(`profiles.switcherTooltip.${scope}`)}
|
||||
className={cn(
|
||||
"inline-flex h-8 items-center gap-1.5 rounded-lg px-2.5 text-sm font-medium transition-colors",
|
||||
@@ -128,6 +131,7 @@ export function ProfileSwitcher({ activeApp }: ProfileSwitcherProps) {
|
||||
side="bottom"
|
||||
align="start"
|
||||
sideOffset={6}
|
||||
aria-label={t(`profiles.switcherTooltip.${scope}`)}
|
||||
className="z-[100] w-64 p-0"
|
||||
>
|
||||
<Command label={t("profiles.searchPlaceholder")}>
|
||||
|
||||
@@ -86,7 +86,12 @@ export function BasicFormFields({
|
||||
<div className="flex-shrink-0 py-4 border-b border-border-default bg-muted/40">
|
||||
<div className="px-6 flex items-center gap-4">
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="outline" size="icon">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
aria-label={t("common.back")}
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
</DialogClose>
|
||||
|
||||
@@ -250,6 +250,7 @@ export function CommonConfigEditor({
|
||||
<JsonEditor
|
||||
value={localValue}
|
||||
onChange={handleLocalChange}
|
||||
ariaLabel={t("provider.configJson")}
|
||||
placeholder={`{
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "https://your-api-endpoint.com",
|
||||
@@ -331,6 +332,7 @@ export function CommonConfigEditor({
|
||||
<JsonEditor
|
||||
value={commonConfigSnippet}
|
||||
onChange={onCommonConfigSnippetChange}
|
||||
ariaLabel={t("claudeConfig.editCommonConfigTitle")}
|
||||
placeholder={`{
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "https://your-api-endpoint.com"
|
||||
|
||||
@@ -81,6 +81,7 @@ export function ProxyToggle({ className, activeApp }: ProxyToggleProps) {
|
||||
checked={takeoverEnabled}
|
||||
onCheckedChange={handleToggle}
|
||||
disabled={isPending || isInitialStatusPending}
|
||||
aria-label={t("proxy.takeover.ariaLabel", { appLabel })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1101,6 +1101,7 @@ const ImportSkillsDialog: React.FC<ImportSkillsDialogProps> = ({
|
||||
type="checkbox"
|
||||
checked={selected.has(skill.directory)}
|
||||
onChange={() => toggleSelect(skill.directory)}
|
||||
aria-label={skill.name}
|
||||
className="mt-1"
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
|
||||
@@ -2200,6 +2200,7 @@
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"switcherAriaLabel": "Project: {{name}}",
|
||||
"switcherTooltip": {
|
||||
"claude": "Projects: switch the Claude Code provider / MCP / Skills / memory files in one click",
|
||||
"claude-desktop": "Projects: switch the Claude Desktop provider in one click",
|
||||
@@ -2883,6 +2884,7 @@
|
||||
},
|
||||
"switchFailed": "Switch failed: {{error}}",
|
||||
"takeover": {
|
||||
"ariaLabel": "{{appLabel}} local routing",
|
||||
"hint": "Select apps to route — once enabled, requests from that app will go through local routing",
|
||||
"enabled": "{{app}} routing enabled",
|
||||
"disabled": "{{app}} routing disabled",
|
||||
|
||||
@@ -2200,6 +2200,7 @@
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"switcherAriaLabel": "プロジェクト:{{name}}",
|
||||
"switcherTooltip": {
|
||||
"claude": "プロジェクト:Claude Code のプロバイダー / MCP / Skills / メモリファイルをワンクリックで切り替え",
|
||||
"claude-desktop": "プロジェクト:Claude Desktop のプロバイダーをワンクリックで切り替え",
|
||||
@@ -2883,6 +2884,7 @@
|
||||
},
|
||||
"switchFailed": "切り替えに失敗しました: {{error}}",
|
||||
"takeover": {
|
||||
"ariaLabel": "{{appLabel}} ローカルルーティング",
|
||||
"hint": "ルーティングするアプリを選択します。有効にすると、そのアプリのリクエストはローカルルーティング経由で転送されます",
|
||||
"enabled": "{{app}} ルーティング有効",
|
||||
"disabled": "{{app}} ルーティング無効",
|
||||
|
||||
@@ -2201,6 +2201,7 @@
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"switcherAriaLabel": "專案:{{name}}",
|
||||
"switcherTooltip": {
|
||||
"claude": "專案:一鍵切換 Claude Code 的供應商 / MCP / Skills / 記憶檔案",
|
||||
"claude-desktop": "專案:一鍵切換 Claude Desktop 的供應商",
|
||||
@@ -2884,6 +2885,7 @@
|
||||
},
|
||||
"switchFailed": "切換失敗:{{error}}",
|
||||
"takeover": {
|
||||
"ariaLabel": "{{appLabel}} 本地路由",
|
||||
"hint": "選擇要路由的應用程式,啟用後該應用程式的請求將透過本地路由轉發",
|
||||
"enabled": "{{app}} 路由已啟用",
|
||||
"disabled": "{{app}} 路由已關閉",
|
||||
|
||||
@@ -2200,6 +2200,7 @@
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"switcherAriaLabel": "项目:{{name}}",
|
||||
"switcherTooltip": {
|
||||
"claude": "项目:一键切换 Claude Code 的供应商 / MCP / Skills / 记忆文件",
|
||||
"claude-desktop": "项目:一键切换 Claude Desktop 的供应商",
|
||||
@@ -2883,6 +2884,7 @@
|
||||
},
|
||||
"switchFailed": "切换失败: {{error}}",
|
||||
"takeover": {
|
||||
"ariaLabel": "{{appLabel}} 本地路由",
|
||||
"hint": "选择要路由的应用,启用后该应用的请求将通过本地路由转发",
|
||||
"enabled": "{{app}} 路由已启用",
|
||||
"disabled": "{{app}} 路由已关闭",
|
||||
|
||||
Reference in new issue
Block a user