diff --git a/packages/factory/src/client/visualizations/catwalk/constants/__tests__/dimensions.test.ts b/packages/factory/src/client/visualizations/catwalk/constants/__tests__/dimensions.test.ts new file mode 100644 index 00000000..3de88109 --- /dev/null +++ b/packages/factory/src/client/visualizations/catwalk/constants/__tests__/dimensions.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it } from 'vitest'; + +import { + AGENT_RADIUS, + ART_H, + ART_W, + CANVAS_H, + CANVAS_W, + CATWALK_Y, + CHUTE_BOT, + CHUTE_TOP, + GATE_W, + GROUND_Y, + LAYOUT_MARGIN, + ORCH_RADIUS, + STATION_GAP, +} from '../dimensions.js'; + +describe('canvas dimensions', () => { + it('has CANVAS_W equal to 1400', () => { + expect(CANVAS_W).toBe(1400); + }); + + it('has CANVAS_H equal to 540', () => { + expect(CANVAS_H).toBe(540); + }); +}); + +describe('derived vertical positions', () => { + it('derives CHUTE_TOP as CATWALK_Y + 48', () => { + expect(CHUTE_TOP).toBe(CATWALK_Y + 48); + }); + + it('derives CHUTE_BOT as GROUND_Y - 20', () => { + expect(CHUTE_BOT).toBe(GROUND_Y - 20); + }); +}); + +describe('anchor values', () => { + it('has CATWALK_Y equal to 100', () => { + expect(CATWALK_Y).toBe(100); + }); + + it('has GROUND_Y equal to 340', () => { + expect(GROUND_Y).toBe(340); + }); +}); + +describe('entity sizing', () => { + it('has positive AGENT_RADIUS', () => { + expect(AGENT_RADIUS).toBeGreaterThan(0); + }); + + it('has ORCH_RADIUS greater than AGENT_RADIUS', () => { + expect(ORCH_RADIUS).toBeGreaterThan(AGENT_RADIUS); + }); + + it('has positive artifact dimensions', () => { + expect(ART_W).toBeGreaterThan(0); + expect(ART_H).toBeGreaterThan(0); + }); + + it('has positive GATE_W', () => { + expect(GATE_W).toBeGreaterThan(0); + }); +}); + +describe('layout margins', () => { + it('has positive LAYOUT_MARGIN', () => { + expect(LAYOUT_MARGIN).toBeGreaterThan(0); + }); + + it('has positive STATION_GAP', () => { + expect(STATION_GAP).toBeGreaterThan(0); + }); +}); diff --git a/packages/factory/src/client/visualizations/catwalk/constants/__tests__/timing.test.ts b/packages/factory/src/client/visualizations/catwalk/constants/__tests__/timing.test.ts new file mode 100644 index 00000000..c0c182bf --- /dev/null +++ b/packages/factory/src/client/visualizations/catwalk/constants/__tests__/timing.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest'; + +import { CHUTE_DURATION, PAUSE_DURATION, WALK_SPEED, WORK_DURATION } from '../timing.js'; + +describe('timing constants', () => { + it('has WALK_SPEED equal to 400 pixels per second', () => { + expect(WALK_SPEED).toBe(400); + }); + + it('has CHUTE_DURATION equal to 600 milliseconds', () => { + expect(CHUTE_DURATION).toBe(600); + }); + + it('has WORK_DURATION equal to 1200 milliseconds', () => { + expect(WORK_DURATION).toBe(1200); + }); + + it('has PAUSE_DURATION equal to 300 milliseconds', () => { + expect(PAUSE_DURATION).toBe(300); + }); +}); diff --git a/packages/factory/src/client/visualizations/catwalk/constants/dimensions.ts b/packages/factory/src/client/visualizations/catwalk/constants/dimensions.ts new file mode 100644 index 00000000..48890cb9 --- /dev/null +++ b/packages/factory/src/client/visualizations/catwalk/constants/dimensions.ts @@ -0,0 +1,20 @@ +// Canvas dimensions +export const CANVAS_W = 1400; +export const CANVAS_H = 540; + +// Vertical positions +export const CATWALK_Y = 100; +export const GROUND_Y = 340; +export const CHUTE_TOP = CATWALK_Y + 48; +export const CHUTE_BOT = GROUND_Y - 20; + +// Entity sizing +export const AGENT_RADIUS = 14; +export const ORCH_RADIUS = 16; +export const ART_W = 40; +export const ART_H = 16; +export const GATE_W = 6; + +// Layout margins +export const LAYOUT_MARGIN = 100; +export const STATION_GAP = 30; diff --git a/packages/factory/src/client/visualizations/catwalk/constants/timing.ts b/packages/factory/src/client/visualizations/catwalk/constants/timing.ts new file mode 100644 index 00000000..5e31a8cf --- /dev/null +++ b/packages/factory/src/client/visualizations/catwalk/constants/timing.ts @@ -0,0 +1,11 @@ +/** Walk speed in pixels per second. */ +export const WALK_SPEED = 400; + +/** Duration of a chute animation in milliseconds. */ +export const CHUTE_DURATION = 600; + +/** Duration of a work animation in milliseconds. */ +export const WORK_DURATION = 1200; + +/** Duration of a pause between actions in milliseconds. */ +export const PAUSE_DURATION = 300; diff --git a/packages/factory/src/shared/constants/__tests__/artifact-colors.test.ts b/packages/factory/src/shared/constants/__tests__/artifact-colors.test.ts new file mode 100644 index 00000000..aa4d9eba --- /dev/null +++ b/packages/factory/src/shared/constants/__tests__/artifact-colors.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'vitest'; + +import { ARTIFACT_COLORS, type ArtifactColorKey } from '../artifact-colors.js'; + +const EXPECTED_KEYS: readonly ArtifactColorKey[] = [ + 'reqs', + 'xplan', + 'arch', + 'plan', + 'code', + 'review', + 'fixes', + 'clean', + 'holi', + 'summary', +]; + +describe('ARTIFACT_COLORS', () => { + it('contains exactly the expected set of keys', () => { + // eslint-disable-next-line unicorn/no-array-sort -- toSorted() unavailable in configured Node range + expect(Object.keys(ARTIFACT_COLORS).sort()).toEqual([...EXPECTED_KEYS].sort()); + }); + + it('maps every key to a valid 6-digit hex color', () => { + for (const value of Object.values(ARTIFACT_COLORS)) { + expect(value).toMatch(/^#[0-9a-f]{6}$/i); + } + }); + + it('maps each key to a distinct color', () => { + const values = Object.values(ARTIFACT_COLORS); + expect(new Set(values).size).toBe(values.length); + }); +}); diff --git a/packages/factory/src/shared/constants/artifact-colors.ts b/packages/factory/src/shared/constants/artifact-colors.ts new file mode 100644 index 00000000..bf9fd544 --- /dev/null +++ b/packages/factory/src/shared/constants/artifact-colors.ts @@ -0,0 +1,15 @@ +/** Mapping from artifact type to pastel display color. */ +export const ARTIFACT_COLORS = { + reqs: '#dee2e6', + xplan: '#d0ebff', + arch: '#a5d8ff', + plan: '#b2f2bb', + code: '#fff3bf', + review: '#ffc9c9', + fixes: '#ffe8cc', + clean: '#f3d9fa', + holi: '#c3fae8', + summary: '#f8f9fa', +} as const; + +export type ArtifactColorKey = keyof typeof ARTIFACT_COLORS;