Merge pull request #5784 from Perlence/fix/threaded-session-latest-activity
fix(coding-agent): sort threaded sessions by latest activity in the subtree
This commit is contained in:
@@ -190,6 +190,7 @@ class SessionSelectorHeader implements Component {
|
|||||||
interface SessionTreeNode {
|
interface SessionTreeNode {
|
||||||
session: SessionInfo;
|
session: SessionInfo;
|
||||||
children: SessionTreeNode[];
|
children: SessionTreeNode[];
|
||||||
|
latestActivity: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Flattened node for display with tree structure info */
|
/** Flattened node for display with tree structure info */
|
||||||
@@ -210,7 +211,7 @@ function buildSessionTree(sessions: SessionInfo[]): SessionTreeNode[] {
|
|||||||
|
|
||||||
for (const session of sessions) {
|
for (const session of sessions) {
|
||||||
const sessionPath = canonicalizePath(session.path) ?? session.path;
|
const sessionPath = canonicalizePath(session.path) ?? session.path;
|
||||||
byPath.set(sessionPath, { session, children: [] });
|
byPath.set(sessionPath, { session, children: [], latestActivity: session.modified.getTime() });
|
||||||
}
|
}
|
||||||
|
|
||||||
const roots: SessionTreeNode[] = [];
|
const roots: SessionTreeNode[] = [];
|
||||||
@@ -227,9 +228,22 @@ function buildSessionTree(sessions: SessionInfo[]): SessionTreeNode[] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort children and roots by modified date (descending)
|
const updateLatestActivity = (node: SessionTreeNode): number => {
|
||||||
|
let latestActivity = node.session.modified.getTime();
|
||||||
|
for (const child of node.children) {
|
||||||
|
latestActivity = Math.max(latestActivity, updateLatestActivity(child));
|
||||||
|
}
|
||||||
|
node.latestActivity = latestActivity;
|
||||||
|
return latestActivity;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const root of roots) {
|
||||||
|
updateLatestActivity(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort children and roots by latest activity in each subtree (descending)
|
||||||
const sortNodes = (nodes: SessionTreeNode[]): void => {
|
const sortNodes = (nodes: SessionTreeNode[]): void => {
|
||||||
nodes.sort((a, b) => b.session.modified.getTime() - a.session.modified.getTime());
|
nodes.sort((a, b) => b.latestActivity - a.latestActivity);
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
sortNodes(node.children);
|
sortNodes(node.children);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,6 +282,45 @@ describe("session selector path/delete interactions", () => {
|
|||||||
expect(output).toContain("└─ Child");
|
expect(output).toContain("└─ Child");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sorts threaded sessions by latest activity in their subtree", async () => {
|
||||||
|
const parentOne = makeSession({
|
||||||
|
id: "parent-one",
|
||||||
|
name: "Parent one",
|
||||||
|
modified: new Date("2026-01-02T00:00:00.000Z"),
|
||||||
|
});
|
||||||
|
const parentTwo = makeSession({
|
||||||
|
id: "parent-two",
|
||||||
|
name: "Parent two",
|
||||||
|
modified: new Date("2026-01-01T00:00:00.000Z"),
|
||||||
|
});
|
||||||
|
const childTwo = makeSession({
|
||||||
|
id: "child-two",
|
||||||
|
name: "Child two",
|
||||||
|
parentSessionPath: parentTwo.path,
|
||||||
|
modified: new Date("2026-01-03T00:00:00.000Z"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const selector = new SessionSelectorComponent(
|
||||||
|
async () => [parentOne, parentTwo, childTwo],
|
||||||
|
async () => [],
|
||||||
|
() => {},
|
||||||
|
() => {},
|
||||||
|
() => {},
|
||||||
|
() => {},
|
||||||
|
{ keybindings },
|
||||||
|
);
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
const output = stripAnsi(selector.render(120).join("\n"));
|
||||||
|
const parentTwoIndex = output.indexOf("Parent two");
|
||||||
|
const childTwoIndex = output.indexOf("└─ Child two");
|
||||||
|
const parentOneIndex = output.indexOf("Parent one");
|
||||||
|
|
||||||
|
expect(parentTwoIndex).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(childTwoIndex).toBeGreaterThan(parentTwoIndex);
|
||||||
|
expect(parentOneIndex).toBeGreaterThan(childTwoIndex);
|
||||||
|
});
|
||||||
|
|
||||||
it("treats the current session as active across symlink aliases", async () => {
|
it("treats the current session as active across symlink aliases", async () => {
|
||||||
const paths = createSymlinkedSessionPaths();
|
const paths = createSymlinkedSessionPaths();
|
||||||
tempDirs.push(paths.baseDir);
|
tempDirs.push(paths.baseDir);
|
||||||
|
|||||||
Reference in New Issue
Block a user