fix(graph): correct particle direction + remove system message filter

Particle direction:
- Added `reverse` flag to GraphParticle — when true, particle flies
  from target → source (reverse of edge direction)
- Messages FROM teammate TO lead now fly member→lead (was lead→member)
- draw-particles.ts swaps from/to nodes when reverse=true

Reverted system message filter:
- Removed #isSystemMessage — all messages shown as particles again
  (user wants to see idle_notification etc.)
This commit is contained in:
iliya 2026-03-30 19:20:53 +03:00
parent 942588093b
commit 485327d077
3 changed files with 21 additions and 19 deletions

View file

@ -37,7 +37,11 @@ export function drawParticles(
if (!source || !target) continue;
if (source.x == null || source.y == null || target.x == null || target.y == null) continue;
const cp = computeControlPoints(source.x, source.y, target.x, target.y);
// Reverse: swap source/target for particles going in opposite direction
const from = p.reverse ? target : source;
const to = p.reverse ? source : target;
const cp = computeControlPoints(from.x!, from.y!, to.x!, to.y!);
const color = p.color || COLORS.message;
const baseSize = (p.size ?? 1) * 3;
// Differentiate visual by particle kind
@ -50,12 +54,12 @@ export function drawParticles(
const phaseOffset = p.id.charCodeAt(Math.min(5, p.id.length - 1)) * 0.1;
const wobbleAmp = BEAM.wobble.amp;
drawParticleTrail(ctx, source, target, cp, p.progress, color, size, wobbleAmp, phaseOffset, time, p.kind);
drawParticleCore(ctx, source, target, cp, p.progress, color, size, wobbleAmp, phaseOffset, time, p.kind);
drawParticleTrail(ctx, from, to, cp, p.progress, color, size, wobbleAmp, phaseOffset, time, p.kind);
drawParticleCore(ctx, from, to, cp, p.progress, color, size, wobbleAmp, phaseOffset, time, p.kind);
// Label
if (p.label && p.progress > PARTICLE_DRAW.labelMinT && p.progress < PARTICLE_DRAW.labelMaxT) {
const pos = getWobbledPosition(source, target, cp, p.progress, wobbleAmp, phaseOffset, time);
const pos = getWobbledPosition(from, to, cp, p.progress, wobbleAmp, phaseOffset, time);
ctx.font = `${PARTICLE_DRAW.labelFontSize}px monospace`;
ctx.textAlign = 'center';
ctx.fillStyle = hexWithAlpha(color, 0.56);

View file

@ -135,6 +135,8 @@ export interface GraphParticle {
size?: number;
/** Short label near particle */
label?: string;
/** If true, particle travels from target → source (reverse direction) */
reverse?: boolean;
}
// ─── Domain Reference (opaque back-pointer) ──────────────────────────────────

View file

@ -494,12 +494,19 @@ export class TeamGraphAdapter {
if (this.#seenMessageIds.has(msgKey)) continue;
this.#seenMessageIds.add(msgKey);
// Skip system/noise messages (idle notifications, JSON blobs)
if (TeamGraphAdapter.#isSystemMessage(msg)) continue;
const edgeId = TeamGraphAdapter.#resolveMessageEdge(msg, teamName, leadId, leadName, edges);
if (!edgeId) continue;
// Determine direction: messages FROM a teammate TO lead should reverse
// (edges are always lead→member, but message goes member→lead)
const fromId = TeamGraphAdapter.#resolveParticipantId(
msg.from ?? '',
teamName,
leadId,
leadName
);
const isFromTeammate = fromId !== leadId;
particles.push({
id: `particle:msg:${teamName}:${msgKey}`,
edgeId,
@ -507,6 +514,7 @@ export class TeamGraphAdapter {
kind: 'inbox_message',
color: msg.color ?? '#66ccff',
label: TeamGraphAdapter.#buildParticleLabel(msg.summary ?? msg.text, 'inbox'),
reverse: isFromTeammate,
});
}
}
@ -694,18 +702,6 @@ export class TeamGraphAdapter {
return `member:${teamName}:${name}`;
}
/** Filter out system/noise messages that shouldn't show as particles */
static #isSystemMessage(msg: InboxMessage): boolean {
const text = msg.text ?? '';
// JSON system messages (idle_notification, shutdown, etc.)
if (text.startsWith('{"type":') || text.startsWith('{"type" :')) return true;
// Very short system messages
if (text.length < 3) return true;
// System notification source
if (msg.source === 'system_notification') return true;
return false;
}
static #buildParticleLabel(
text: string | undefined,
kind: 'inbox' | 'comment',