refactor: unify workspace tab management and add runtime metrics

This commit is contained in:
Mohamed Boudra
2026-03-05 14:21:16 +07:00
parent 6dc49ccbc7
commit 8371ea0c7d
32 changed files with 1064 additions and 461 deletions
@@ -53,7 +53,7 @@ function resolveWorkspaceAgentTabLabel(title: string | null | undefined): string
return normalized;
}
function normalizeUiTab(tab: WorkspaceTab): WorkspaceTab | null {
function normalizeWorkspaceTab(tab: WorkspaceTab): WorkspaceTab | null {
if (!tab || typeof tab !== "object") {
return null;
}
@@ -75,6 +75,28 @@ function normalizeUiTab(tab: WorkspaceTab): WorkspaceTab | null {
createdAt: tab.createdAt,
};
}
if (tab.target.kind === "agent") {
const agentId = trimNonEmpty(tab.target.agentId);
if (!agentId) {
return null;
}
return {
tabId,
target: { kind: "agent", agentId },
createdAt: tab.createdAt,
};
}
if (tab.target.kind === "terminal") {
const terminalId = trimNonEmpty(tab.target.terminalId);
if (!terminalId) {
return null;
}
return {
tabId,
target: { kind: "terminal", terminalId },
createdAt: tab.createdAt,
};
}
if (tab.target.kind === "file") {
const path = trimNonEmpty(tab.target.path);
if (!path) {
@@ -105,53 +127,20 @@ export function buildWorkspaceTabId(target: WorkspaceTabTarget): string {
export function deriveWorkspaceTabModel(input: {
workspaceAgents: Agent[];
terminals: TerminalLike[];
uiTabs: WorkspaceTab[];
tabs: WorkspaceTab[];
tabOrder: string[];
focusedTabId?: string | null;
}): WorkspaceTabModel {
const tabsById = new Map<string, WorkspaceDerivedTab>();
const agentsById = new Map(input.workspaceAgents.map((agent) => [agent.id, agent]));
const terminalsById = new Map(input.terminals.map((terminal) => [terminal.id, terminal]));
for (const agent of input.workspaceAgents) {
const target: WorkspaceTabTarget = { kind: "agent", agentId: agent.id };
const tabId = buildWorkspaceTabId(target);
const label = resolveWorkspaceAgentTabLabel(agent.title);
tabsById.set(tabId, {
target,
descriptor: {
key: tabId,
tabId,
kind: "agent",
agentId: agent.id,
provider: agent.provider,
label: label ?? "",
subtitle: `${formatProviderLabel(agent.provider)} agent`,
titleState: label ? "ready" : "loading",
},
});
}
for (const terminal of input.terminals) {
const target: WorkspaceTabTarget = { kind: "terminal", terminalId: terminal.id };
const tabId = buildWorkspaceTabId(target);
tabsById.set(tabId, {
target,
descriptor: {
key: tabId,
tabId,
kind: "terminal",
terminalId: terminal.id,
label: trimNonEmpty(terminal.name) ?? "Terminal",
subtitle: "Terminal",
},
});
}
const normalizedUiTabs = input.uiTabs
.map((tab) => normalizeUiTab(tab))
const normalizedTabs = input.tabs
.map((tab) => normalizeWorkspaceTab(tab))
.filter((tab): tab is WorkspaceTab => tab !== null)
.sort((left, right) => left.createdAt - right.createdAt);
for (const tab of normalizedUiTabs) {
for (const tab of normalizedTabs) {
if (tab.target.kind === "draft") {
tabsById.set(tab.tabId, {
target: tab.target,
@@ -167,6 +156,42 @@ export function deriveWorkspaceTabModel(input: {
continue;
}
if (tab.target.kind === "agent") {
const agent = agentsById.get(tab.target.agentId) ?? null;
const label = resolveWorkspaceAgentTabLabel(agent?.title);
const provider = agent?.provider ?? "codex";
tabsById.set(tab.tabId, {
target: tab.target,
descriptor: {
key: tab.tabId,
tabId: tab.tabId,
kind: "agent",
agentId: tab.target.agentId,
provider,
label: label ?? "",
subtitle: `${formatProviderLabel(provider)} agent`,
titleState: label ? "ready" : "loading",
},
});
continue;
}
if (tab.target.kind === "terminal") {
const terminal = terminalsById.get(tab.target.terminalId) ?? null;
tabsById.set(tab.tabId, {
target: tab.target,
descriptor: {
key: tab.tabId,
tabId: tab.tabId,
kind: "terminal",
terminalId: tab.target.terminalId,
label: trimNonEmpty(terminal?.name ?? null) ?? "Terminal",
subtitle: "Terminal",
},
});
continue;
}
if (tab.target.kind === "file") {
const filePath = tab.target.path;
const fileName = filePath.split("/").filter(Boolean).pop() ?? filePath;