Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aeed658946 | |||
| e656221c0e | |||
| c8e10b88f9 | |||
| b6adc26086 | |||
| 40c8883736 | |||
| e8cefcaa8a |
@@ -205,6 +205,7 @@ export function MobileCanvas({
|
||||
type="button"
|
||||
onClick={resetView}
|
||||
aria-label="Reset zoom"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 14,
|
||||
@@ -272,6 +273,8 @@ export function MobileCanvas({
|
||||
key={l.agent.id}
|
||||
type="button"
|
||||
onClick={() => onOpen(l.agent.id)}
|
||||
aria-label={`Open ${l.agent.name}`}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: `${l.x}%`,
|
||||
@@ -376,6 +379,7 @@ export function MobileCanvas({
|
||||
type="button"
|
||||
onClick={onSpawn}
|
||||
aria-label="Spawn new agent"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2"
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 24,
|
||||
|
||||
@@ -339,6 +339,7 @@ export function MobileChat({
|
||||
type="button"
|
||||
onClick={onBack}
|
||||
aria-label="Back"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
width: 36,
|
||||
height: 36,
|
||||
@@ -385,6 +386,7 @@ export function MobileChat({
|
||||
<button
|
||||
type="button"
|
||||
aria-label="More"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
width: 36,
|
||||
height: 36,
|
||||
@@ -402,35 +404,62 @@ export function MobileChat({
|
||||
</button>
|
||||
</div>
|
||||
{/* Sub-tabs */}
|
||||
<div style={{ display: "flex", gap: 18, marginTop: 12, paddingLeft: 4 }}>
|
||||
{(
|
||||
[
|
||||
{ id: "my", label: "My Chat" },
|
||||
{ id: "a2a", label: "Agent Comms" },
|
||||
] as const
|
||||
).map((t) => {
|
||||
const on = tab === t.id;
|
||||
return (
|
||||
<button
|
||||
key={t.id}
|
||||
type="button"
|
||||
onClick={() => setTab(t.id)}
|
||||
style={{
|
||||
padding: "4px 0 8px",
|
||||
border: "none",
|
||||
background: "transparent",
|
||||
fontSize: 13.5,
|
||||
cursor: "pointer",
|
||||
color: on ? p.text : p.text3,
|
||||
fontWeight: on ? 600 : 500,
|
||||
borderBottom: on ? `2px solid ${p.accent}` : "2px solid transparent",
|
||||
}}
|
||||
>
|
||||
{t.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{(
|
||||
[
|
||||
{ id: "my", label: "My Chat" },
|
||||
{ id: "a2a", label: "Agent Comms" },
|
||||
] as const
|
||||
).map((t) => {
|
||||
const on = tab === t.id;
|
||||
return (
|
||||
<button
|
||||
key={t.id}
|
||||
role="tab"
|
||||
type="button"
|
||||
tabIndex={on ? 0 : -1}
|
||||
aria-selected={on}
|
||||
onClick={() => setTab(t.id)}
|
||||
onKeyDown={(e) => {
|
||||
const tabs = ["my", "a2a"] as const;
|
||||
const idx = tabs.indexOf(t.id);
|
||||
let nextIdx: number | null = null;
|
||||
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
||||
nextIdx = (idx + 1) % tabs.length;
|
||||
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
||||
nextIdx = (idx - 1 + tabs.length) % tabs.length;
|
||||
} else if (e.key === "Home") {
|
||||
nextIdx = 0;
|
||||
} else if (e.key === "End") {
|
||||
nextIdx = tabs.length - 1;
|
||||
}
|
||||
if (nextIdx !== null) {
|
||||
e.preventDefault();
|
||||
setTab(tabs[nextIdx]!);
|
||||
setTimeout(() => {
|
||||
const btns = document.querySelectorAll('[role="tab"]');
|
||||
(btns[nextIdx!] as HTMLButtonElement | null)?.focus();
|
||||
}, 0);
|
||||
}
|
||||
}}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
padding: "4px 0 8px",
|
||||
marginTop: 12,
|
||||
marginLeft: 4,
|
||||
marginRight: 14,
|
||||
border: "none",
|
||||
background: "transparent",
|
||||
fontSize: 13.5,
|
||||
cursor: "pointer",
|
||||
color: on ? p.text : p.text3,
|
||||
fontWeight: on ? 600 : 500,
|
||||
borderBottom: on ? `2px solid ${p.accent}` : "2px solid transparent",
|
||||
}}
|
||||
>
|
||||
{t.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Messages */}
|
||||
@@ -478,6 +507,7 @@ export function MobileChat({
|
||||
onClick={() => {
|
||||
loadInitial();
|
||||
}}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
padding: "6px 14px",
|
||||
borderRadius: 14,
|
||||
@@ -619,6 +649,7 @@ export function MobileChat({
|
||||
type="button"
|
||||
onClick={() => removePendingFile(i)}
|
||||
aria-label={`Remove ${f.name}`}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
border: "none",
|
||||
background: "transparent",
|
||||
@@ -659,6 +690,7 @@ export function MobileChat({
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={!reachable || sending || uploading}
|
||||
aria-label="Attach"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
@@ -703,7 +735,9 @@ export function MobileChat({
|
||||
border: "none",
|
||||
outline: "none",
|
||||
background: "transparent",
|
||||
fontSize: 14.5,
|
||||
// 16px minimum prevents iOS from zooming the page when the
|
||||
// textarea receives focus (iOS triggers zoom for font-size < 16).
|
||||
fontSize: 16,
|
||||
lineHeight: 1.4,
|
||||
color: p.text,
|
||||
padding: "6px 0",
|
||||
@@ -719,6 +753,7 @@ export function MobileChat({
|
||||
onClick={send}
|
||||
disabled={(!draft.trim() && pendingFiles.length === 0) || !reachable || sending || uploading}
|
||||
aria-label="Send"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
width: 36,
|
||||
height: 36,
|
||||
|
||||
@@ -218,6 +218,7 @@ export function MobileComms({ dark }: { dark: boolean }) {
|
||||
key={o.id}
|
||||
type="button"
|
||||
onClick={() => setFilter(o.id)}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
|
||||
@@ -83,11 +83,12 @@ export function MobileDetail({
|
||||
type="button"
|
||||
onClick={onBack}
|
||||
aria-label="Back"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={iconButtonStyle(p, dark)}
|
||||
>
|
||||
{Icons.back({ size: 18 })}
|
||||
</button>
|
||||
<button type="button" aria-label="More" style={iconButtonStyle(p, dark)}>
|
||||
<button type="button" aria-label="More" className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1" style={iconButtonStyle(p, dark)}>
|
||||
{Icons.more({ size: 18 })}
|
||||
</button>
|
||||
</div>
|
||||
@@ -168,6 +169,8 @@ export function MobileDetail({
|
||||
|
||||
{/* Tabs */}
|
||||
<div
|
||||
role="tablist"
|
||||
aria-label="Agent detail sections"
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
@@ -181,8 +184,33 @@ export function MobileDetail({
|
||||
return (
|
||||
<button
|
||||
key={t.id}
|
||||
role="tab"
|
||||
type="button"
|
||||
tabIndex={on ? 0 : -1}
|
||||
aria-selected={on}
|
||||
onClick={() => setTab(t.id)}
|
||||
onKeyDown={(e) => {
|
||||
const idx = TABS.findIndex((x) => x.id === t.id);
|
||||
let nextIdx: number | null = null;
|
||||
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
||||
nextIdx = (idx + 1) % TABS.length;
|
||||
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
||||
nextIdx = (idx - 1 + TABS.length) % TABS.length;
|
||||
} else if (e.key === "Home") {
|
||||
nextIdx = 0;
|
||||
} else if (e.key === "End") {
|
||||
nextIdx = TABS.length - 1;
|
||||
}
|
||||
if (nextIdx !== null) {
|
||||
e.preventDefault();
|
||||
setTab(TABS[nextIdx]!.id);
|
||||
setTimeout(() => {
|
||||
const btns = document.querySelectorAll('[role="tab"]');
|
||||
(btns[nextIdx!] as HTMLButtonElement | null)?.focus();
|
||||
}, 0);
|
||||
}
|
||||
}}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
padding: "8px 14px",
|
||||
borderRadius: 999,
|
||||
@@ -215,6 +243,7 @@ export function MobileDetail({
|
||||
type="button"
|
||||
onClick={onChat}
|
||||
data-testid="mobile-chat-cta"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 52,
|
||||
|
||||
@@ -183,6 +183,7 @@ export function MobileHome({
|
||||
type="button"
|
||||
onClick={onSpawn}
|
||||
aria-label="Spawn new agent"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2"
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 24,
|
||||
|
||||
@@ -83,6 +83,7 @@ export function MobileMe({
|
||||
type="button"
|
||||
onClick={() => setAccent(c)}
|
||||
aria-label={`Set accent ${c}`}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
width: 36,
|
||||
height: 36,
|
||||
@@ -173,6 +174,7 @@ function SegmentedRow({
|
||||
key={o.id}
|
||||
type="button"
|
||||
onClick={() => onChange(o.id)}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: "10px 8px",
|
||||
|
||||
@@ -148,6 +148,7 @@ export function MobileSpawn({ dark, onClose }: { dark: boolean; onClose: () => v
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
@@ -214,6 +215,7 @@ export function MobileSpawn({ dark, onClose }: { dark: boolean; onClose: () => v
|
||||
setTplId(t.id);
|
||||
setTier(tCode);
|
||||
}}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
background: on
|
||||
? dark
|
||||
@@ -286,7 +288,7 @@ export function MobileSpawn({ dark, onClose }: { dark: boolean; onClose: () => v
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{Icons.check({ size: 10, sw: 2.5 })}
|
||||
<span aria-hidden="true">{Icons.check({ size: 10, sw: 2.5 })}</span>
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
@@ -330,6 +332,7 @@ export function MobileSpawn({ dark, onClose }: { dark: boolean; onClose: () => v
|
||||
key={t}
|
||||
type="button"
|
||||
onClick={() => setTier(t)}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: "10px 8px",
|
||||
@@ -377,6 +380,7 @@ export function MobileSpawn({ dark, onClose }: { dark: boolean; onClose: () => v
|
||||
type="button"
|
||||
onClick={handleSpawn}
|
||||
disabled={busy || !tplId || templates.length === 0}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 52,
|
||||
|
||||
@@ -291,6 +291,7 @@ export function AgentCard({
|
||||
data-testid="workspace-card"
|
||||
aria-label={`${agent.name}, status: ${agent.status}, tier ${agent.tier}${agent.remote ? ", remote" : ""}`}
|
||||
onClick={onClick}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
display: "block",
|
||||
width: "100%",
|
||||
@@ -444,6 +445,7 @@ export function FilterChips({
|
||||
type="button"
|
||||
aria-checked={on}
|
||||
onClick={() => onChange(o.id)}
|
||||
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1"
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
|
||||
Reference in New Issue
Block a user