refactor(org): drop legacy adapterLabels / claude_compat fallback; unify connector label resolution

OrgChart and AgentRoster both carried a `claude_compat`-prefix fallback
chained against `adapterLabels[connectorId] ?? connectorId` to paper
over the pre-pi-mono connector id space (claude_local / codex_local /
gemini_local / claude_compat_2..10). The migration to pi-mono's catalog
retired those ids months ago — the fallback is dead code that still
runs `String.prototype.startsWith` on every render and forks the label
resolution into two subtly different shapes in two files.

Replaced with `formatConnectorLabel(connector, fallbackId)` in
lib/formatters.ts, used by both OrgChart and AgentRoster.

Also in this commit (typography polish bundled because touching the
same render paths):

- OrgChart connector label: 9px uppercase tracking-0.08em → 11px
  tracking-tight. The 9px chip was unreadable at default zoom.
- OrgChart zoom controls: `+` / `−` / "Fit" mixed text + character ops
  replaced with Plus / Minus / CornersOut icons (imported from the
  existing icon barrel) + a focus-visible ring for keyboard users.

No rendering changes for the non-cosmetic path (status dots, card
layout, edges). The removed `adapterLabels` map was exported from
orgTreeLayout.ts and had no other importers.

Covers J1 / J3 / J4 / J6 from the team-page audit.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-17 19:43:12 -04:00
1 parent 41132e2da4
commit 50a7196b3e
5 files changed
+29 -26

No files matched your search

@@ -5,6 +5,7 @@ import type { AgentRecord, ConnectorRecord, WorkspaceRecord } from "@shared/type
import { EmptyState, StatusPill } from "../ui"
import { useT } from "../../i18n"
import { useRovingTabIndex } from "../../lib/keyboard"
import { formatConnectorLabel } from "../../lib/formatters"
import { BudgetBar } from "./helpers"
export function AgentRoster({
@@ -96,7 +97,7 @@ export function AgentRoster({
<StatusPill status={agent.status} />
</div>
<div className="flex items-center justify-between text-[11px] text-muted-foreground">
<span>{t("org.connectorLabel")} {connector?.id.startsWith("claude_compat") && connector.notes ? connector.notes : connector?.label ?? agent.connectorId}</span>
<span>{t("org.connectorLabel")} {formatConnectorLabel(connector, agent.connectorId)}</span>
{workspace && <span>{t("org.workspaceLabel")} {workspace.name}</span>}
</div>
<div className="mt-1.5">
@@ -1,10 +1,10 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Heartbeat } from "../ui/icons";
import { CornersOut, Heartbeat, Minus, Plus } from "../ui/icons";
import type { AgentRecord, ConnectorRecord } from "@shared/types";
import { getAgentInitials } from "../../lib/formatters";
import { formatConnectorLabel, getAgentInitials } from "../../lib/formatters";
import { EmptyState } from "../ui";
import { useT } from "../../i18n";
import { buildOrgTree, layoutForest, flattenLayout, collectEdges, statusDotColor, adapterLabels, CARD_W, CARD_H, CHART_PADDING } from "./orgTreeLayout";
import { buildOrgTree, layoutForest, flattenLayout, collectEdges, statusDotColor, CARD_W, CARD_H, CHART_PADDING } from "./orgTreeLayout";
export function OrgChart({ agents, connectors, onSelectAgent }: { agents: AgentRecord[]; connectors?: ConnectorRecord[]; onSelectAgent?: (agentId: string) => void }) {
const t = useT();
@@ -75,8 +75,14 @@ export function OrgChart({ agents, connectors, onSelectAgent }: { agents: AgentR
return (
<div ref={containerRef} className="relative min-h-[400px] h-[clamp(400px,60vh,720px)] w-full overflow-hidden rounded-lg border border-border bg-background" style={{ cursor: dragging ? "grabbing" : "grab" }} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} onMouseLeave={handleMouseUp} onWheel={handleWheel}>
<div className="absolute right-3 top-3 z-10 flex flex-col gap-1">
{[{ label: "+", ariaLabel: t("org.zoomIn"), action: zoomIn }, { label: "\u2212", ariaLabel: t("org.zoomOut"), action: zoomOut }, { label: t("org.fit"), ariaLabel: t("org.fitChart"), action: fitToScreen }].map((btn) => (
<button key={btn.ariaLabel} className="flex h-9 w-9 items-center justify-center rounded-lg border border-border bg-card text-[12px] text-foreground transition hover:bg-muted" onClick={btn.action} aria-label={btn.ariaLabel}>{btn.label}</button>
{[
{ icon: Plus, ariaLabel: t("org.zoomIn"), action: zoomIn },
{ icon: Minus, ariaLabel: t("org.zoomOut"), action: zoomOut },
{ icon: CornersOut, ariaLabel: t("org.fitChart"), action: fitToScreen },
].map(({ icon: Icon, ariaLabel, action }) => (
<button key={ariaLabel} className="flex h-9 w-9 items-center justify-center rounded-lg border border-border bg-card text-foreground transition hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" onClick={action} aria-label={ariaLabel}>
<Icon size={16} weight="regular" />
</button>
))}
</div>
<svg className="pointer-events-none absolute inset-0" style={{ width: "100%", height: "100%" }}>
@@ -93,9 +99,7 @@ export function OrgChart({ agents, connectors, onSelectAgent }: { agents: AgentR
{allNodes.map((node) => {
const dotColor = statusDotColor[node.agent.status] ?? "var(--muted-foreground)";
const connectorRecord = connectors?.find((c) => c.id === node.agent.connectorId);
const connectorLabel = (connectorRecord?.id.startsWith("claude_compat") && connectorRecord.notes)
? connectorRecord.notes
: adapterLabels[node.agent.connectorId] ?? node.agent.connectorId;
const connectorLabel = formatConnectorLabel(connectorRecord, node.agent.connectorId);
const initials = getAgentInitials(node.agent.name);
const isRunning = node.agent.status === "running";
const isActive = node.agent.status === "active" || isRunning;
@@ -122,7 +126,7 @@ export function OrgChart({ agents, connectors, onSelectAgent }: { agents: AgentR
<span className="truncate text-[14px] font-semibold leading-tight text-foreground">{node.agent.name}</span>
<span className="mt-0.5 text-[11px] leading-tight text-muted-foreground">{node.agent.title || node.agent.role}</span>
<div className="mt-1.5 flex items-center gap-2">
<span className="rounded bg-muted px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">{connectorLabel}</span>
<span className="rounded bg-muted px-1.5 py-0.5 text-[11px] font-semibold tracking-tight text-muted-foreground">{connectorLabel}</span>
{node.agent.heartbeatEnabled ? <Heartbeat size={10} className="text-success" /> : null}
</div>
</div>
@@ -94,19 +94,3 @@ export const statusDotColor: Record<string, string> = {
terminated: "var(--muted-foreground)",
pending_approval: "var(--destructive)",
};
export const adapterLabels: Record<string, string> = {
claude_local: "Claude",
claude_compatible: "Compatible",
claude_compat_2: "Compatible 2",
claude_compat_3: "Compatible 3",
claude_compat_4: "Compatible 4",
claude_compat_5: "Compatible 5",
claude_compat_6: "Compatible 6",
claude_compat_7: "Compatible 7",
claude_compat_8: "Compatible 8",
claude_compat_9: "Compatible 9",
claude_compat_10: "Compatible 10",
codex_local: "Codex",
gemini_local: "Gemini",
};
+1
View File
@@ -43,6 +43,7 @@ export {
Clock,
ClockCounterClockwise,
Command,
CornersOut,
Copy,
Cpu,
CurrencyCircleDollar,
+13
View File
@@ -46,6 +46,19 @@ export function getAgentInitials(name: string): string {
.toUpperCase();
}
/**
* Resolve the user-visible label for a connector reference. The org chart
* and agent roster share this so there is one definition of "what do I
* show for `agent.connectorId`?" — prefer the registered label, fall
* back to the raw id when a connector row is missing (e.g. pending sync).
*/
export function formatConnectorLabel(
connector: { label?: string | null; id?: string | null } | null | undefined,
fallbackConnectorId: string,
): string {
return connector?.label || connector?.id || fallbackConnectorId;
}
export function formatDuration(seconds: number): string {
if (seconds < 60) return `${seconds}s`;
const minutes = Math.floor(seconds / 60);