fix(canvas): add focus-visible rings to final 5 component files

- A2AEdge.tsx: edge label pill button
- OrgCancelButton.tsx: cancel trigger + confirm Yes/No
- AttachmentTextPreview.tsx: download, show-all, truncated-dl buttons
- form-inputs.tsx: tag remove (red) + section toggle (accent)
- secrets-section.tsx: SecretRow/CustomSecretRow remove (red), update,
  save, scope toggle (amber for global), add-variable buttons
- settings-panel.css: UnsavedChangesGuard keep/discard buttons

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Molecule AI · core-uiux 2026-05-10 17:26:45 +00:00 committed by Molecule AI Core-FE
parent cd0207b224
commit ee8eec8213
6 changed files with 30 additions and 20 deletions

View File

@ -119,7 +119,7 @@ function A2AEdgeImpl({
onClick={handleClick}
aria-label={ariaLabel}
title="Open source workspace's activity feed"
className={`px-2 py-0.5 rounded-full bg-surface-sunken/95 border ${accent} ${accentText} text-[10px] font-medium shadow-md shadow-black/40 backdrop-blur-sm hover:bg-surface-card hover:border-opacity-100 transition-colors cursor-pointer`}
className={`px-2 py-0.5 rounded-full bg-surface-sunken/95 border ${accent} ${accentText} text-[10px] font-medium shadow-md shadow-black/40 backdrop-blur-sm hover:bg-surface-card hover:border-opacity-100 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1`}
>
{labelText}
</button>

View File

@ -122,7 +122,7 @@ export function OrgCancelButton({ rootId, rootName, workspaceCount }: Props) {
type="button"
onClick={handleCancel}
disabled={submitting}
className="mol-deploy-cancel px-2 py-0.5 rounded text-[10px] font-semibold"
className="mol-deploy-cancel px-2 py-0.5 rounded text-[10px] font-semibold focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-1"
>
{submitting ? "Deleting…" : "Yes"}
</button>
@ -130,7 +130,7 @@ export function OrgCancelButton({ rootId, rootName, workspaceCount }: Props) {
type="button"
onClick={() => setConfirming(false)}
disabled={submitting}
className="px-2 py-0.5 rounded bg-surface-card/80 hover:bg-surface-card text-[10px] text-ink"
className="px-2 py-0.5 rounded bg-surface-card/80 hover:bg-surface-card text-[10px] text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
>
No
</button>
@ -148,7 +148,7 @@ export function OrgCancelButton({ rootId, rootName, workspaceCount }: Props) {
e.stopPropagation();
setConfirming(true);
}}
className="nodrag mol-deploy-cancel mol-deploy-cancel-pulse absolute -top-7 right-1 z-20 flex items-center gap-1 rounded-full px-2.5 py-0.5 text-[10px] font-semibold shadow-md"
className="nodrag mol-deploy-cancel mol-deploy-cancel-pulse absolute -top-7 right-1 z-20 flex items-center gap-1 rounded-full px-2.5 py-0.5 text-[10px] font-semibold shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-1"
aria-label={`Cancel deployment of ${rootName}`}
>
<svg width="10" height="10" viewBox="0 0 16 16" aria-hidden="true">

View File

@ -148,7 +148,7 @@ export function AttachmentTextPreview({ workspaceId, attachment, onDownload, ton
<button
type="button"
onClick={() => onDownload(attachment)}
className="text-ink-mid hover:text-ink"
className="text-ink-mid hover:text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
title={`Download ${attachment.name}`}
aria-label={`Download ${attachment.name}`}
>
@ -162,7 +162,7 @@ export function AttachmentTextPreview({ workspaceId, attachment, onDownload, ton
<button
type="button"
onClick={() => setExpanded(true)}
className="block w-full text-center text-[10px] text-ink-mid hover:text-ink py-1 border-t border-line/40"
className="block w-full text-center text-[10px] text-ink-mid hover:text-ink py-1 border-t border-line/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
>
Show all {lines.length} lines
</button>
@ -173,7 +173,7 @@ export function AttachmentTextPreview({ workspaceId, attachment, onDownload, ton
<button
type="button"
onClick={() => onDownload(attachment)}
className="underline"
className="underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
>
download full file
</button>

View File

@ -102,7 +102,7 @@ export function TagList({ label, values, onChange, placeholder }: { label: strin
{values.map((v, i) => (
<span key={i} className="inline-flex items-center gap-1 px-1.5 py-0.5 bg-surface-card border border-line rounded text-[10px] text-ink-mid font-mono">
{v}
<button type="button" aria-label={`Remove tag ${v}`} onClick={() => onChange(values.filter((_, j) => j !== i))} className="text-ink-mid hover:text-bad">×</button>
<button type="button" aria-label={`Remove tag ${v}`} onClick={() => onChange(values.filter((_, j) => j !== i))} className="text-ink-mid hover:text-bad focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-1">×</button>
</span>
))}
</div>
@ -129,7 +129,7 @@ export function Section({ title, children, defaultOpen = true }: { title: string
const [open, setOpen] = useState(defaultOpen);
return (
<div className="border border-line rounded mb-2">
<button type="button" onClick={() => setOpen(!open)} className="w-full flex items-center justify-between px-3 py-1.5 text-[10px] text-ink-mid hover:text-ink bg-surface-sunken/50">
<button type="button" onClick={() => setOpen(!open)} className="w-full flex items-center justify-between px-3 py-1.5 text-[10px] text-ink-mid hover:text-ink bg-surface-sunken/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1">
<span className="font-medium uppercase tracking-wider">{title}</span>
<span>{open ? "▾" : "▸"}</span>
</button>

View File

@ -113,9 +113,9 @@ function SecretRow({ label, secretKey, isSet, scope, globalMode, onSave, onDelet
{isSet && <span className="text-[10px] text-good bg-green-900/30 px-1.5 py-0.5 rounded">Set</span>}
{scope && <ScopeBadge scope={scope} />}
{!editing && isSet && (globalMode || scope !== "global") && (
<button type="button" onClick={onDelete} className="text-[11px] text-bad hover:text-bad">Remove</button>
<button type="button" onClick={onDelete} className="text-[11px] text-bad hover:text-bad focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-1">Remove</button>
)}
<button type="button" onClick={() => setEditing(!editing)} className="text-[11px] text-accent hover:text-accent">
<button type="button" onClick={() => setEditing(!editing)} className="text-[11px] text-accent hover:text-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1">
{actionLabel()}
</button>
</div>
@ -131,7 +131,7 @@ function SecretRow({ label, secretKey, isSet, scope, globalMode, onSave, onDelet
<button type="button"
onClick={() => { onSave(value); setEditing(false); setValue(""); }}
disabled={!value}
className="px-2 py-1 bg-accent-strong hover:bg-accent text-[10px] rounded text-white disabled:opacity-30"
className="px-2 py-1 bg-accent-strong hover:bg-accent text-[10px] rounded text-white disabled:opacity-30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
>Save</button>
</div>
)}
@ -165,10 +165,10 @@ function CustomSecretRow({ secretKey, scope, globalMode, onSave, onDelete }: {
<span className="text-[10px] text-good">Set</span>
{!globalMode && <ScopeBadge scope={scope} />}
{canDelete && !editing && (
<button type="button" onClick={onDelete} className="text-[11px] text-bad hover:text-bad">Remove</button>
<button type="button" onClick={onDelete} className="text-[11px] text-bad hover:text-bad focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-1">Remove</button>
)}
{(canDelete || showOverride) && (
<button type="button" onClick={() => setEditing(!editing)} className="text-[11px] text-accent hover:text-accent">
<button type="button" onClick={() => setEditing(!editing)} className="text-[11px] text-accent hover:text-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1">
{editing ? "Cancel" : showOverride ? "Override" : "Update"}
</button>
)}
@ -184,7 +184,7 @@ function CustomSecretRow({ secretKey, scope, globalMode, onSave, onDelete }: {
<button type="button"
onClick={() => { onSave(value); setEditing(false); setValue(""); }}
disabled={!value}
className="px-2 py-1 bg-accent-strong hover:bg-accent text-[10px] rounded text-white disabled:opacity-30"
className="px-2 py-1 bg-accent-strong hover:bg-accent text-[10px] rounded text-white disabled:opacity-30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
>Save</button>
</div>
)}
@ -297,7 +297,7 @@ export function SecretsSection({ workspaceId, requiredEnv }: { workspaceId: stri
<div className="flex items-center gap-2 pb-1">
<button
onClick={() => setGlobalMode(false)}
className={`text-[10px] px-2 py-0.5 rounded transition-colors ${
className={`text-[10px] px-2 py-0.5 rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 ${
!globalMode ? "bg-accent-strong/20 text-accent border border-accent/30" : "text-white-soft hover:text-white-mid"
}`}
>
@ -305,7 +305,7 @@ export function SecretsSection({ workspaceId, requiredEnv }: { workspaceId: stri
</button>
<button
onClick={() => setGlobalMode(true)}
className={`text-[10px] px-2 py-0.5 rounded transition-colors ${
className={`text-[10px] px-2 py-0.5 rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-400 focus-visible:ring-offset-1 ${
globalMode ? "bg-amber-600/20 text-warm border border-amber-500/30" : "text-white-soft hover:text-white-mid"
}`}
>
@ -356,15 +356,15 @@ export function SecretsSection({ workspaceId, requiredEnv }: { workspaceId: stri
className="w-full bg-surface-sunken border border-line rounded px-2 py-1 text-[10px] text-ink focus:outline-none focus:border-accent" />
<div className="flex gap-2">
<button type="button" onClick={() => { if (newKey && newValue) handleSave(newKey, newValue); }} disabled={!newKey || !newValue}
className="px-2 py-1 bg-accent-strong hover:bg-accent text-[10px] rounded text-white disabled:opacity-30">
className="px-2 py-1 bg-accent-strong hover:bg-accent text-[10px] rounded text-white disabled:opacity-30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1">
Save{globalMode ? " (Global)" : ""}
</button>
<button type="button" onClick={() => { setShowAdd(false); setNewKey(""); setNewValue(""); }}
className="px-2 py-1 bg-surface-card hover:bg-surface-card text-[10px] rounded text-ink-mid">Cancel</button>
className="px-2 py-1 bg-surface-card hover:bg-surface-card text-[10px] rounded text-ink-mid focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1">Cancel</button>
</div>
</div>
) : (
<button type="button" onClick={() => setShowAdd(true)} className="text-[10px] text-accent hover:text-accent">
<button type="button" onClick={() => setShowAdd(true)} className="text-[10px] text-accent hover:text-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1">
+ Add {globalMode ? "Global " : ""}Variable
</button>
)}

View File

@ -684,6 +684,11 @@
cursor: pointer;
}
.guard-dialog__keep-btn:focus-visible {
outline: var(--focus-ring);
outline-offset: var(--focus-ring-offset);
}
.guard-dialog__discard-btn {
background: #2563eb;
color: #ffffff;
@ -693,6 +698,11 @@
cursor: pointer;
}
.guard-dialog__discard-btn:focus-visible {
outline: var(--focus-ring);
outline-offset: var(--focus-ring-offset);
}
/* ── Settings button (top bar) ─────────────────────── */
.settings-button {