style(client): widen the agent card grid

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.
This commit is contained in:
bowenliang123 committed 2026-10-10 12:00:41 +08:00
1 parent 38fe279213
commit cd26173459
3 files changed
+18 -14

No files matched your search

+3 -2
View File
@@ -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. */
+12 -9
View File
@@ -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', () => {
+3 -3
View File
@@ -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)