mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-20 09:09:35 +02:00
fix(tabs): children spawned from one parent keep creation order (#528 merge-time fix)
With the follow setting off (the default), pinCreatedChild marked each new child manual where normalization put it: right after the parent's FOLLOWING block, which no longer holds the siblings pinned before it. A fan-out therefore showed parent, w3, w2, w1 in the layout and the global sessionOrder. The pinned child now steps past the siblings (and their descendants) already after the parent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a38eab49cd
commit
5ac27f3eec
2 files changed
+44
-1
No files matched your search
@@ -131,7 +131,31 @@ function pinCreatedChild(layout: TabLayout, sessionId: string, metadata: readonl
|
||||
const parentStored = refs.some((candidate) => candidate.kind === 'session' && candidate.id === parentId);
|
||||
const parentValid = metadata.some((fact) => fact.kind === 'session' && fact.id === parentId && fact.ownerValid);
|
||||
if (!ref || ref.placement === 'manual' || !parentStored || !parentValid) return layout;
|
||||
return setManualPlacement(layout, ref, true);
|
||||
const pinned = setManualPlacement(layout, ref, true);
|
||||
// Normalization put the child right after its parent's FOLLOWING block, i.e.
|
||||
// ahead of the siblings pinned before it. Step past those (and their
|
||||
// descendants) so a fan-out keeps creation order.
|
||||
const parentOf = new Map<string, string>();
|
||||
for (const fact of metadata) {
|
||||
if (fact.kind === 'session' && fact.ownerValid && fact.parentSessionId) parentOf.set(fact.id, fact.parentSessionId);
|
||||
}
|
||||
const descendsFromParent = (id: string): boolean => {
|
||||
let current = parentOf.get(id);
|
||||
for (let hops = 0; current !== undefined && hops <= parentOf.size; hops++) {
|
||||
if (current === parentId) return true;
|
||||
current = parentOf.get(current);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const isChild = (candidate: TabRef): boolean => candidate.kind === 'session' && candidate.id === sessionId;
|
||||
const group = pinned.groups.find((candidate) => candidate.refs.some(isChild));
|
||||
const container = group ? group.refs : pinned.ungrouped;
|
||||
const from = container.findIndex(isChild);
|
||||
let to = from;
|
||||
while (to + 1 < container.length && container[to + 1].kind === 'session' && descendsFromParent(container[to + 1].id))
|
||||
to++;
|
||||
if (to === from) return pinned;
|
||||
return moveRef(pinned, { kind: 'session', id: sessionId }, { groupId: group?.id ?? null, index: to }, metadata);
|
||||
}
|
||||
|
||||
/** Move a just-created session to the end of `groupId`, or leave the layout alone. */
|
||||
|
||||
@@ -1438,6 +1438,25 @@ describe('TabLayoutService grouped session creation', () => {
|
||||
expect(moved.layout.ungrouped.map((ref) => ref.id)).toEqual(['b', 'a']);
|
||||
});
|
||||
|
||||
it('with the follow setting off, children spawned one after another keep creation order', async () => {
|
||||
const h = createHarness({ layouts: { alice: grouped() }, live: facts() });
|
||||
let layout = grouped();
|
||||
for (const [index, id] of ['c1', 'c2', 'c3'].entries()) {
|
||||
layout = await h.service.sessionCreated(
|
||||
'alice',
|
||||
id,
|
||||
{},
|
||||
register(h, { id, owner: 'alice', createdAt: 3 + index, parentSessionId: 'a' })
|
||||
);
|
||||
}
|
||||
expect(layout.groups[0].refs).toEqual([
|
||||
{ kind: 'session', id: 'a' },
|
||||
{ kind: 'session', id: 'c1', placement: 'manual' },
|
||||
{ kind: 'session', id: 'c2', placement: 'manual' },
|
||||
{ kind: 'session', id: 'c3', placement: 'manual' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('reads the follow setting fresh at every creation', async () => {
|
||||
const h = createHarness({ layouts: { alice: grouped() }, live: facts() });
|
||||
const setting = vi.fn<() => boolean>().mockReturnValueOnce(true).mockReturnValueOnce(false);
|
||||
|
||||
Reference in new issue
Block a user