fix(agent-hub-ui): corrected agent hub render flow in editorContainer
- Fixed Agent Hub display to render in editorContainer instead of floating ui overlay. - Fixed close flow to clear editorContainer, re-add the editor, and restore editor focus. - Updated Agent Hub activation tests and removed the probe test fixture.
This commit is contained in:
@@ -1216,11 +1216,20 @@ export class SelectorController {
|
||||
...this.ctx.keybindings.getKeys("app.session.observe"),
|
||||
];
|
||||
let hub: AgentHubOverlayComponent | undefined;
|
||||
let overlayHandle: OverlayHandle | undefined;
|
||||
|
||||
// Render the hub inline in the editor slot — the same anchored region
|
||||
// every other selector (model, session, tree, the `ask` tool) uses —
|
||||
// rather than a floating overlay. A non-fullscreen overlay composited over
|
||||
// a live transcript strands a stale copy in native scrollback every time a
|
||||
// running subagent's progress grows the frame and scrolls the window; the
|
||||
// hub is opened mid-run, so those copies stacked into a wall of duplicate
|
||||
// "Agent Hub" frames bleeding the task tree behind them. As an editor-slot
|
||||
// component it rides the normal append-only commit path: the transcript
|
||||
// commits above it exactly once and the hub repaints in place.
|
||||
const done = () => {
|
||||
hub?.dispose();
|
||||
overlayHandle?.hide();
|
||||
this.ctx.editorContainer.clear();
|
||||
this.ctx.editorContainer.addChild(this.ctx.editor);
|
||||
this.ctx.ui.setFocus(this.ctx.editor);
|
||||
this.ctx.ui.requestRender();
|
||||
};
|
||||
@@ -1251,12 +1260,8 @@ export class SelectorController {
|
||||
return;
|
||||
}
|
||||
|
||||
overlayHandle = this.ctx.ui.showOverlay(hub, {
|
||||
anchor: "bottom-center",
|
||||
width: "100%",
|
||||
maxHeight: "100%",
|
||||
margin: 0,
|
||||
});
|
||||
this.ctx.editorContainer.clear();
|
||||
this.ctx.editorContainer.addChild(hub);
|
||||
this.ctx.ui.setFocus(hub);
|
||||
this.ctx.ui.requestRender();
|
||||
}
|
||||
|
||||
@@ -125,18 +125,21 @@ describe("Agent hub Enter activation", () => {
|
||||
|
||||
const editor = {};
|
||||
let capturedHub: AgentHubOverlayComponent | undefined;
|
||||
let hideCalls = 0;
|
||||
let editorRestoredCount = 0;
|
||||
const focusedIds: string[] = [];
|
||||
const focusResolved = Promise.withResolvers<void>();
|
||||
const editorFocused = Promise.withResolvers<void>();
|
||||
const focusTargets: unknown[] = [];
|
||||
const editorContainer = {
|
||||
clear: () => {},
|
||||
addChild: (child: unknown) => {
|
||||
if (child === editor) editorRestoredCount++;
|
||||
else capturedHub = child as AgentHubOverlayComponent;
|
||||
},
|
||||
};
|
||||
const ctx = {
|
||||
keybindings: { getKeys: () => [] },
|
||||
ui: {
|
||||
showOverlay: (component: AgentHubOverlayComponent) => {
|
||||
capturedHub = component;
|
||||
return { hide: () => hideCalls++ };
|
||||
},
|
||||
setFocus: (target: unknown) => {
|
||||
focusTargets.push(target);
|
||||
if (target === editor) editorFocused.resolve();
|
||||
@@ -144,6 +147,7 @@ describe("Agent hub Enter activation", () => {
|
||||
requestRender: () => {},
|
||||
},
|
||||
editor,
|
||||
editorContainer,
|
||||
collabGuest: { agentRegistry: agents, hubRemote: undefined },
|
||||
focusAgentSession: async (id: string) => {
|
||||
focusedIds.push(id);
|
||||
@@ -165,7 +169,7 @@ describe("Agent hub Enter activation", () => {
|
||||
await editorFocused.promise;
|
||||
|
||||
expect(focusedIds).toEqual([AGENT_ID]);
|
||||
expect(hideCalls).toBe(1);
|
||||
expect(editorRestoredCount).toBe(1);
|
||||
expect(focusTargets.at(-1)).toBe(editor);
|
||||
capturedHub!.dispose();
|
||||
});
|
||||
@@ -182,17 +186,20 @@ describe("Agent hub double-← gating", () => {
|
||||
|
||||
function setup(agents: AgentRegistry) {
|
||||
let shown: AgentHubOverlayComponent | undefined;
|
||||
const editor = {};
|
||||
const ctx = {
|
||||
keybindings: { getKeys: () => [] },
|
||||
ui: {
|
||||
showOverlay: (component: AgentHubOverlayComponent) => {
|
||||
shown = component;
|
||||
return { hide: () => {} };
|
||||
},
|
||||
setFocus: () => {},
|
||||
requestRender: () => {},
|
||||
},
|
||||
editor: {},
|
||||
editor,
|
||||
editorContainer: {
|
||||
clear: () => {},
|
||||
addChild: (child: unknown) => {
|
||||
if (child !== editor) shown = child as AgentHubOverlayComponent;
|
||||
},
|
||||
},
|
||||
collabGuest: { agentRegistry: agents, hubRemote: undefined },
|
||||
focusAgentSession: async () => {},
|
||||
session: { getToolByName: () => undefined, extensionRunner: undefined },
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { beforeAll, describe, it } from "bun:test";
|
||||
import { IrcBus } from "@oh-my-pi/pi-coding-agent/irc/bus";
|
||||
import { AgentHubOverlayComponent } from "@oh-my-pi/pi-coding-agent/modes/components/agent-hub";
|
||||
import { SessionObserverRegistry } from "@oh-my-pi/pi-coding-agent/modes/session-observer-registry";
|
||||
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import { AgentRegistry } from "@oh-my-pi/pi-coding-agent/registry/agent-registry";
|
||||
import type { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
|
||||
describe("hub probe", () => {
|
||||
beforeAll(async () => {
|
||||
await initTheme();
|
||||
});
|
||||
|
||||
it("dumps rendered rows", () => {
|
||||
Object.defineProperty(process.stdout, "rows", { configurable: true, get: () => 40 });
|
||||
Object.defineProperty(process.stdout, "columns", { configurable: true, get: () => 120 });
|
||||
const agents = new AgentRegistry();
|
||||
const sess = {} as AgentSession;
|
||||
for (const letter of ["A", "B", "C", "D", "E"]) {
|
||||
const id = `Repro${letter}`;
|
||||
agents.register({
|
||||
id,
|
||||
displayName: `Advisor delivery bug reproducer (independent track ${letter})`,
|
||||
kind: "sub",
|
||||
parentId: id, // self-parent, mimicking the bug
|
||||
session: sess,
|
||||
status: "running",
|
||||
});
|
||||
}
|
||||
const hub = new AgentHubOverlayComponent({
|
||||
observers: new SessionObserverRegistry(),
|
||||
hubKeys: [],
|
||||
onDone: () => {},
|
||||
requestRender: () => {},
|
||||
registry: agents,
|
||||
irc: new IrcBus(agents),
|
||||
focusAgent: async () => {},
|
||||
});
|
||||
const lines = hub.render(120).map(l => Bun.stripANSI(l));
|
||||
console.error("===HUB RENDER START===");
|
||||
for (const l of lines) console.error(JSON.stringify(l));
|
||||
console.error("===HUB RENDER END===");
|
||||
hub.dispose();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user