diff --git a/.gitignore b/.gitignore index 57d318f1..b630c704 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ out/ .vercel # Local-only +.local/ local/ tmp/ *.local diff --git a/packages/factory/package.json b/packages/factory/package.json index 8ba9a9be..d712cd22 100644 --- a/packages/factory/package.json +++ b/packages/factory/package.json @@ -9,6 +9,7 @@ "dev": "tsx src/server/index.ts & pnpm vite --host", "dev:client": "pnpm vite --host", "dev:server": "tsx --watch src/server/index.ts", + "generate-sprites": "tsx scripts/generate-placeholder-pngs.ts", "validate:run-index": "tsx src/scripts/validate-run-index.ts", "ws": "node --import tsx ../../scripts/run-workspace-script.ts" }, diff --git a/packages/factory/scripts/generate-placeholder-pngs.ts b/packages/factory/scripts/generate-placeholder-pngs.ts index 08ef7f8e..5e277297 100644 --- a/packages/factory/scripts/generate-placeholder-pngs.ts +++ b/packages/factory/scripts/generate-placeholder-pngs.ts @@ -12,6 +12,7 @@ import { join } from 'node:path'; import { ORCHESTRATOR_POSES } from './sprites/orchestrator-poses.ts'; import { ORCHESTRATOR_PALETTE, SUBAGENT_PALETTE } from './sprites/palettes.ts'; +import { renderPreviewHtml } from './sprites/preview-renderer.ts'; import { SUBAGENT_POSES } from './sprites/subagent-poses.ts'; import { renderSpriteSheet } from './sprites/svg-renderer.ts'; @@ -25,4 +26,16 @@ const orchestratorSvg = renderSpriteSheet(ORCHESTRATOR_POSES, ORCHESTRATOR_PALET writeFileSync(join(outDir, 'subagent.svg'), subagentSvg); writeFileSync(join(outDir, 'orchestrator.svg'), orchestratorSvg); +const previewHtml = renderPreviewHtml(subagentSvg, orchestratorSvg); +const previewDir = join(import.meta.dirname, '../../../.local/sprites'); +mkdirSync(previewDir, { recursive: true }); +const timestamp = new Date() + .toISOString() + .replace(/[-:]/g, '') + .replace('T', '-') + .replace(/\.\d+Z$/, 'Z'); +const previewPath = join(previewDir, `${timestamp}_sprite-preview.html`); +writeFileSync(previewPath, previewHtml); + console.info('Sprite sheets written to', outDir); +console.info('Preview written to', previewPath); diff --git a/packages/factory/scripts/sprites/__tests__/preview-renderer.test.ts b/packages/factory/scripts/sprites/__tests__/preview-renderer.test.ts new file mode 100644 index 00000000..b6fd8f7e --- /dev/null +++ b/packages/factory/scripts/sprites/__tests__/preview-renderer.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, it } from 'vitest'; + +import { renderPreviewHtml } from '../preview-renderer.ts'; + +const STUB_SUBAGENT_SVG = 'subagent-stub'; +const STUB_ORCHESTRATOR_SVG = 'orchestrator-stub'; + +describe('renderPreviewHtml', () => { + const html = renderPreviewHtml(STUB_SUBAGENT_SVG, STUB_ORCHESTRATOR_SVG); + + // ── Document structure ────────────────────────────────────────────────── + + it('returns a string starting with ', () => { + expect(html).toMatch(/^/); + }); + + it('contains , and tags', () => { + expect(html).toContain(''); + expect(html).toContain(''); + }); + + // ── Styling ───────────────────────────────────────────────────────────── + + it('uses the midnight blue background color #1a1a2e', () => { + expect(html).toContain('#1a1a2e'); + }); + + // ── Section labels ────────────────────────────────────────────────────── + + it('contains a subagent section label', () => { + expect(html).toContain('

subagent

'); + }); + + it('contains an orchestrator section label', () => { + expect(html).toContain('

orchestrator

'); + }); + + // ── Frame labels ──────────────────────────────────────────────────────── + + const expectedLabels = [ + 'Idle 1', + 'Idle 2', + 'Walking', + 'Resting 1', + 'Working 1', + 'Working 2', + 'Working 3', + 'Resting 2', + 'Celebrating 1', + 'Celebrating 2', + 'Concerned', + 'Resting 3', + ]; + + for (const label of expectedLabels) { + it(`contains the frame label "${label}"`, () => { + expect(html).toContain(label); + }); + } + + // ── Animation script ─────────────────────────────────────────────────── + + it('contains a + +`; +}