From cd26173459c0f14cdaab4fad703cb37e96e93ecc Mon Sep 17 00:00:00 2001 From: bowenliang123 Date: Sat, 10 Oct 2026 12:00:41 +0800 Subject: [PATCH] style(client): widen the agent card grid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The natural cell pitch was 208px, which left the two-line titles on a subagent's card hugging the edge. Every card takes the same, larger pitch — 276px — so the graph stays one grid and no card claims room its neighbour does not get. A stage narrower than the pitch still compresses it, so the family never overflows horizontally. --- src/client/agentTree.ts | 5 +++-- tests/client/agentTree.spec.ts | 21 ++++++++++++--------- tests/client/components/agentGraph.spec.ts | 6 +++--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/client/agentTree.ts b/src/client/agentTree.ts index 8e86992..e3ab8d2 100644 --- a/src/client/agentTree.ts +++ b/src/client/agentTree.ts @@ -87,8 +87,9 @@ export const AGENT_TREE_LIMIT = 25 * exit point (card bottom) and entry point (card top) always land on the card's edge. */ export const AGENT_CARD_H = 104 /** Horizontal cell pitch adapts to the stage width between these bounds; each node owns one - * cell and the card fills it minus a 12px gutter, so a card never clips a neighbor or the stage edge. */ -const SLOT_MAX = 208 + * cell and the card fills it minus a 12px gutter, so a card never clips a neighbor or the stage edge. + * Every card shares one pitch — the graph reads as a grid, and a card never claims extra room. */ +const SLOT_MAX = 276 const SLOT_MIN = 150 const CARD_GUTTER = 12 /** Vertical pitch between depth levels: card height + a dedicated 40px link channel below it. */ diff --git a/tests/client/agentTree.spec.ts b/tests/client/agentTree.spec.ts index 0f9de0e..f6374b6 100644 --- a/tests/client/agentTree.spec.ts +++ b/tests/client/agentTree.spec.ts @@ -524,6 +524,9 @@ describe('subagentCostFoldOf', () => { }) describe('layoutForest', () => { + /** The natural cell pitch (agentTree.ts SLOT_MAX) — the width a roomy stage settles on. */ + const SLOT = 276 + function forestOf(ids: [string, string?][]): AgentForest { const nodes = ids.map(([id, parent], i) => ({ id, @@ -572,19 +575,19 @@ describe('layoutForest', () => { assert.equal(layout.points.length, 1) assert.equal(layout.points[0].depth, 0) assert.equal(layout.links.length, 0) - assert.equal(layout.width, 208) + assert.equal(layout.width, SLOT) assert.equal(layout.height, 48 + 104 + 24) - assert.equal(layout.cardW, 208 - 12) + assert.equal(layout.cardW, SLOT - 12) }) test('levels follow depth, siblings claim leaf slots, parents center over children', () => { const layout = layoutForest(forestOf([['root'], ['a', 'root'], ['b', 'root'], ['g', 'a']])) const pointOf = new Map(layout.points.map(p => [p.id, p])) // Two leaf cells (g under a, then b); root centers between them. - assert.equal(pointOf.get('g')?.x, 104) - assert.equal(pointOf.get('a')?.x, 104) - assert.equal(pointOf.get('b')?.x, 104 + 208) - assert.equal(pointOf.get('root')?.x, 104 + 104) + assert.equal(pointOf.get('g')?.x, SLOT / 2) + assert.equal(pointOf.get('a')?.x, SLOT / 2) + assert.equal(pointOf.get('b')?.x, SLOT / 2 + SLOT) + assert.equal(pointOf.get('root')?.x, SLOT) // One row per depth level. assert.equal(pointOf.get('root')?.y, 48) assert.equal(pointOf.get('a')?.y, 48 + 144) @@ -602,15 +605,15 @@ describe('layoutForest', () => { assert.equal(linkG.color, familyHue(0)) assert.equal(layout.links.find(l => l.to === 'b')?.color, familyHue(1)) assert.equal(layout.links.length, 3) - assert.equal(layout.width, 2 * 208) + assert.equal(layout.width, 2 * SLOT) assert.equal(layout.height, 48 + 2 * 144 + 104 + 24) }) test('responsive: a wide stage keeps the natural pitch', () => { const forest = forestOf([['root'], ['a', 'root'], ['b', 'root'], ['c', 'root']]) const layout = layoutForest(forest, 4000) - assert.equal(layout.width, 3 * 208) - assert.equal(layout.cardW, 208 - 12) + assert.equal(layout.width, 3 * SLOT) + assert.equal(layout.cardW, SLOT - 12) }) test('responsive: a tighter stage compresses the slot pitch to fit exactly', () => { diff --git a/tests/client/components/agentGraph.spec.ts b/tests/client/components/agentGraph.spec.ts index 7dd2c8a..9cff505 100644 --- a/tests/client/components/agentGraph.spec.ts +++ b/tests/client/components/agentGraph.spec.ts @@ -173,11 +173,11 @@ describe('AgentGraph — the family tree', () => { assert.ok(query(m.container, '[data-agent="worker"]').querySelector('.lc-agent-self-badge') === null) // Links join both children as bezier curves out of the root card's foot - // (x=208: root centers over the two leaf slots) with joint plugs at both ends; - // the running one layers a flowing pulse over the solid lineage stroke. + // (x=276: the natural slot pitch, with the root centered over the two leaf slots) + // with joint plugs at both ends; the running one layers a flowing pulse over the solid lineage stroke. const links = queryAll(m.container, 'path.lc-agents-link') assert.equal(links.length, 2) - assert.ok(links.every(l => l.getAttribute('d')?.startsWith('M 208 '))) + assert.ok(links.every(l => l.getAttribute('d')?.startsWith('M 276 '))) assert.ok(links.every(l => l.getAttribute('d')?.includes(' C '))) assert.equal(queryAll(m.container, 'path.lc-agents-link-live').length, 1) assert.equal(queryAll(m.container, 'path.lc-agents-flow').length, 1)