- Cross-team messages now show ghost nodes (dashed hexagons) for external teams - Ghost nodes have purple color, link icon, and connect to lead via message edge - Particles flow between ghost node and lead with cross-team message labels - Cross-team popover shows external team name - Task click opens full KanbanTaskCard with glow effects and action buttons - All kanban task actions wired through CustomEvent to TeamDetailView
249 lines
7.5 KiB
TypeScript
249 lines
7.5 KiB
TypeScript
/**
|
||
* Canvas rendering constants for the agent graph visualization.
|
||
* Adapted from agent-flow's canvas-constants.ts (Apache 2.0).
|
||
* Stripped of unused features (tool cards, discoveries, cost overlays, bubbles).
|
||
*/
|
||
|
||
// ─── Visibility threshold ───────────────────────────────────────────────────
|
||
|
||
export const MIN_VISIBLE_OPACITY = 0.05;
|
||
|
||
// ─── Animation speed multipliers (× deltaTime) ─────────────────────────────
|
||
|
||
export const ANIM_SPEED = {
|
||
agentFadeIn: 3,
|
||
agentScaleIn: 4,
|
||
agentFadeOut: 0.4,
|
||
agentScaleOut: 0.05,
|
||
edgeFadeIn: 4,
|
||
particleSpeed: 1.2,
|
||
maxDeltaTime: 0.1,
|
||
defaultDeltaTime: 0.016,
|
||
/** Task pill fade in/out */
|
||
taskFadeIn: 3,
|
||
taskFadeOut: 0.6,
|
||
} as const;
|
||
|
||
// ─── Camera / interaction ───────────────────────────────────────────────────
|
||
|
||
export const CAMERA = {
|
||
zoomStepDown: 0.92,
|
||
zoomStepUp: 1.08,
|
||
minZoom: 0.15,
|
||
maxZoom: 5,
|
||
velocityScale: 0.016,
|
||
} as const;
|
||
|
||
// ─── Force simulation ───────────────────────────────────────────────────────
|
||
|
||
export const FORCE = {
|
||
chargeStrength: -800,
|
||
centerStrength: 0.03,
|
||
collideRadius: 100,
|
||
linkDistance: {
|
||
'parent-child': 500,
|
||
ownership: 150,
|
||
blocking: 200,
|
||
related: 200,
|
||
message: 300,
|
||
},
|
||
linkStrength: 0.4,
|
||
alphaDecay: 0.02,
|
||
velocityDecay: 0.4,
|
||
} as const;
|
||
|
||
// ─── Node sizes ─────────────────────────────────────────────────────────────
|
||
|
||
export const NODE = {
|
||
/** Lead agent radius */
|
||
radiusLead: 32,
|
||
/** Team member radius */
|
||
radiusMember: 24,
|
||
/** Process node radius */
|
||
radiusProcess: 14,
|
||
/** Cross-team ghost node radius */
|
||
radiusCrossTeam: 20,
|
||
} as const;
|
||
|
||
// ─── Task pill dimensions ───────────────────────────────────────────────────
|
||
|
||
export const TASK_PILL = {
|
||
width: 160,
|
||
height: 36,
|
||
borderRadius: 6,
|
||
statusDotRadius: 4,
|
||
statusDotX: 12,
|
||
/** Font size for display ID */
|
||
idFontSize: 9,
|
||
/** Font size for subject text */
|
||
subjectFontSize: 7,
|
||
/** Max chars for subject before truncation */
|
||
subjectMaxChars: 18,
|
||
/** X offset for text content */
|
||
textOffsetX: 20,
|
||
} as const;
|
||
|
||
// ─── Agent drawing constants ────────────────────────────────────────────────
|
||
|
||
export const AGENT_DRAW = {
|
||
glowPadding: 20,
|
||
outerRingOffset: 3,
|
||
shadowBlur: 15,
|
||
shadowOffsetX: 3,
|
||
shadowOffsetY: 5,
|
||
labelYOffset: 8,
|
||
labelWidthMultiplier: 3,
|
||
scanlineHalfH: 4,
|
||
waitingDashSpeed: 25,
|
||
orbitParticleOffset: 12,
|
||
orbitParticleSize: 1.5,
|
||
rippleInnerOffset: 5,
|
||
rippleMaxExpand: 45,
|
||
rippleMaxAlpha: 0.4,
|
||
waitingOrbitOffset: 14,
|
||
waitingOrbitParticleSize: 2,
|
||
waitingOrbitSpeed: 0.8,
|
||
waitingBreatheSpeed: 1.2,
|
||
waitingBreatheAmp: 0.08,
|
||
sparkScale: 0.45,
|
||
sparkViewBox: 256,
|
||
subIconScale: 0.45,
|
||
} as const;
|
||
|
||
// ─── Context ring (lead node only) ─────────────────────────────────────────
|
||
|
||
export const CONTEXT_RING = {
|
||
ringOffset: 8,
|
||
ringWidth: 4,
|
||
warningThreshold: 0.8,
|
||
criticalThreshold: 0.9,
|
||
percentLabelThreshold: 0.7,
|
||
glowPadding: 4,
|
||
glowLineWidth: 2,
|
||
glowBlur: 12,
|
||
percentYOffset: 10,
|
||
} as const;
|
||
|
||
// ─── Edge/beam drawing ──────────────────────────────────────────────────────
|
||
|
||
export const BEAM = {
|
||
curvature: 0.15,
|
||
cp1: 0.33,
|
||
cp2: 0.66,
|
||
segments: 16,
|
||
parentChild: { startW: 3, endW: 1 },
|
||
ownership: { startW: 2, endW: 0.8 },
|
||
blocking: { startW: 2, endW: 1.5 },
|
||
related: { startW: 1, endW: 0.5 },
|
||
message: { startW: 1.5, endW: 0.5 },
|
||
glowExtra: { startW: 3, endW: 1, alpha: 0.08 },
|
||
idleAlpha: 0.08,
|
||
activeAlpha: 0.3,
|
||
wobble: { amp: 3, freq: 10, timeFreq: 3, trailOffset: 0.15 },
|
||
} as const;
|
||
|
||
// ─── Animation constants ────────────────────────────────────────────────────
|
||
|
||
export const ANIM = {
|
||
inertiaDecay: 0.94,
|
||
inertiaThreshold: 0.5,
|
||
dragLerp: 0.25,
|
||
autoFitLerp: 0.06,
|
||
dragThresholdPx: 5,
|
||
viewportPadding: 120,
|
||
breathe: {
|
||
activeSpeed: 2,
|
||
activeAmp: 0.03,
|
||
idleSpeed: 0.7,
|
||
idleAmp: 0.015,
|
||
},
|
||
scanline: { active: 40, normal: 15 },
|
||
orbitSpeed: 1.5,
|
||
pulseSpeed: 4,
|
||
} as const;
|
||
|
||
// ─── Visual effects ─────────────────────────────────────────────────────────
|
||
|
||
export const FX = {
|
||
spawnDuration: 0.8,
|
||
completeDuration: 1.0,
|
||
shatterDuration: 0.8,
|
||
shatterCount: 12,
|
||
shatterSpeed: { min: 30, range: 60 },
|
||
shatterSize: { min: 1, range: 2 },
|
||
trailSegments: 8,
|
||
} as const;
|
||
|
||
export const SPAWN_FX = {
|
||
ringStart: 10,
|
||
ringExpand: 60,
|
||
maxAlpha: 0.7,
|
||
flashThreshold: 0.3,
|
||
flashAlpha: 0.6,
|
||
flashBaseRadius: 20,
|
||
flashMinRadius: 5,
|
||
particleCount: 8,
|
||
particleSize: 1.5,
|
||
} as const;
|
||
|
||
export const COMPLETE_FX = {
|
||
ringStart: 20,
|
||
ringExpand: 80,
|
||
maxAlpha: 0.6,
|
||
flashThreshold: 0.2,
|
||
flashAlpha: 0.8,
|
||
flashRadius: 30,
|
||
lineWidthMax: 3,
|
||
glowInner: 5,
|
||
glowOuter: 10,
|
||
} as const;
|
||
|
||
// ─── Particle drawing ───────────────────────────────────────────────────────
|
||
|
||
export const PARTICLE_DRAW = {
|
||
glowRadius: 15,
|
||
coreHighlightScale: 0.4,
|
||
labelMinT: 0.2,
|
||
labelMaxT: 0.8,
|
||
labelFontSize: 8,
|
||
labelYOffset: -12,
|
||
/** Seconds a particle lives before fading */
|
||
lifetime: 2.0,
|
||
} as const;
|
||
|
||
// ─── Hit detection ──────────────────────────────────────────────────────────
|
||
|
||
export const HIT_DETECTION = {
|
||
/** Extra padding around nodes for easier clicking */
|
||
agentPadding: 8,
|
||
/** Task pill hit area padding */
|
||
taskPadding: 4,
|
||
} as const;
|
||
|
||
// ─── Background ─────────────────────────────────────────────────────────────
|
||
|
||
export const BACKGROUND = {
|
||
/** Number of depth particles (stars) */
|
||
starCount: 80,
|
||
/** Hex grid cell size */
|
||
hexSize: 30,
|
||
/** Hex grid max alpha */
|
||
hexAlpha: 0.08,
|
||
/** Hex grid pulse speed */
|
||
hexPulseSpeed: 0.3,
|
||
} as const;
|
||
|
||
// ─── Kanban zone layout ─────────────────────────────────────────────────────
|
||
|
||
export const KANBAN_ZONE = {
|
||
/** Column width: pill (160) + gap (20) */
|
||
columnWidth: 180,
|
||
/** Row height: pill (36) + gap (10) */
|
||
rowHeight: 46,
|
||
/** Zone starts this far below member node center */
|
||
offsetY: 70,
|
||
/** Column order: todo → wip → done → review → approved */
|
||
columns: ['todo', 'wip', 'done', 'review', 'approved'] as const,
|
||
/** Max tasks shown per column (overflow hidden) */
|
||
maxVisibleRows: 6,
|
||
} as const;
|