- Remove legacy template.svg files from example/ and my-project/ - Simplify generator.js by removing generateHardcoded method (326→210 lines, -36%) - Add strict template validation with clear error messages - Remove all fallback mechanisms - template-v2.svg format now required - Clean up tests: remove hardcoded generation tests, keep template-based tests - Add comprehensive e2e tests (large datasets, edge cases, error handling) - Update documentation: mark REFACTORING_PLAN.md complete, add TEMPLATE_V2_GUIDE.md - All 56 tests passing (16 engine + 25 generator + 15 integration) BREAKING CHANGE: Old template.svg format no longer supported. Must use template-v2.svg with <g id="*-template"> elements in defs section. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
7.5 KiB
SVG Template Refactoring Plan
✅ STATUS: COMPLETED
Completion Date: 2025-11-27 Final Architecture: Template-only SVG generation with no fallback mechanisms
All phases have been successfully completed:
- ✅ Phase 1: Template-v2.svg files created and working
- ✅ Phase 2: Template-based generation fully implemented
- ✅ Phase 3: Old templates removed, hardcoded generation eliminated
- ✅ Comprehensive test coverage: 40+ tests (unit + integration)
- ✅ Documentation complete (see TEMPLATE_V2_GUIDE.md)
Original Architecture Problems (Resolved)
The Issue
While template.svg files exist in example/ and my-project/, they only contain:
- Outer SVG wrapper with styling
- Two macros:
{{MONTHS}}and{{LANES}}
The actual SVG generation is hardcoded in generator.js:
- Lines 40-69: Month grid generation (lines, labels, styling)
- Lines 78-129: Lane generation (backgrounds, labels, task items)
This makes templates non-editable in SVG tools - users can only change colors/styling in the wrapper, not the actual timeline structure.
Goals
- Move SVG structure to templates: Extract hardcoded SVG patterns from JavaScript to template files
- Use placeholders for dynamic data: Replace actual values with
{{VARIABLE}}placeholders - Make templates editable: Users should be able to open templates in Inkscape/Adobe Illustrator and modify the timeline layout
- Maintain flexibility: Keep the ability to handle varying numbers of months, lanes, and items
Proposed Template Structure
Month Template Section
Instead of generating months in JS, the template should contain a sample month element with placeholders:
<!-- Sample month element - will be cloned for each month -->
<g class="month-template" id="month-template">
<line x1="{{MONTH_X}}" y1="{{GRID_TOP}}" x2="{{MONTH_X}}" y2="{{GRID_BOTTOM}}"
stroke="#E3E8EF" />
<text x="{{MONTH_X}}" y="{{MONTH_Y}}" fill="#5C6B7A" font-size="12">
{{MONTH_LABEL}}
</text>
</g>
Lane Template Section
Similarly, lanes should be defined once:
<!-- Sample lane element - will be cloned for each lane -->
<g class="lane-template" id="lane-template">
<rect x="{{LANE_X}}" y="{{LANE_Y}}" width="{{LANE_WIDTH}}" height="{{LANE_HEIGHT}}"
fill="#FFFFFF" stroke="#E3E8EF" rx="10" />
<text x="{{LABEL_X}}" y="{{LABEL_Y}}" fill="#0B1F3B" font-size="14" font-weight="600">
{{LANE_NAME}}
</text>
</g>
Task Item Template
Task items within lanes:
<!-- Sample task item - will be cloned for each task -->
<g class="task-template" id="task-template">
<circle cx="{{TASK_X}}" cy="{{TASK_Y}}" r="5" fill="#0A4D8C" />
<text x="{{TEXT_X}}" y="{{TEXT_Y}}" font-size="12" fill="#0B1F3B">
<tspan class="item-id">{{TASK_ID}}: </tspan>
<tspan class="item-title">{{TASK_TITLE}}</tspan>
</text>
</g>
Generator Refactoring
The generator.js should:
- Load and parse template: Read template SVG
- Extract template elements: Find elements with
id="*-template" - Clone and populate: For each data item, clone the template and replace placeholders
- Position elements: Calculate positions based on layout constants
- Inject into template: Replace
{{MONTHS}}and{{LANES}}macros with generated content
Variables Dictionary
Layout Constants
{{GRID_LEFT}},{{GRID_TOP}},{{GRID_BOTTOM}}- Grid boundaries{{MONTH_WIDTH}}- Width of each month column{{LANE_HEIGHT}}- Height of each lane row{{LANE_GAP}}- Spacing between lanes
Month Variables
{{MONTH_X}}- X position{{MONTH_Y}}- Y position for label{{MONTH_LABEL}}- Month name (e.g., "Jan 25")
Lane Variables
{{LANE_X}},{{LANE_Y}}- Lane position{{LANE_WIDTH}},{{LANE_HEIGHT}}- Lane dimensions{{LANE_NAME}}- Lane/Epic name{{LABEL_X}},{{LABEL_Y}}- Label position
Task Variables
{{TASK_X}},{{TASK_Y}}- Task marker position{{TEXT_X}},{{TEXT_Y}}- Text position{{TASK_ID}}- Task ID (e.g., "T-1"){{TASK_TITLE}}- Task title
Migration Strategy
Phase 1: Extract to Simple Templates
- Create new
template-v2.svgfiles with template elements - Update generator to use template-based approach
- Keep existing templates as fallback
- Test with both approaches
Phase 2: Enhance Template Editing
- Add template validation
- Document template variables
- Create template editing guide
- Provide example templates with different layouts
Phase 3: Remove Hardcoded SVG
- Migrate existing templates to new format
- Remove hardcoded SVG generation
- Update tests
- Update documentation
Test Updates Needed
- Tests for template parsing and validation
- Tests for variable substitution
- Tests for template cloning logic
- Integration tests with real templates
- Tests for backward compatibility with old templates
Benefits
- User-editable templates: Can modify in any SVG editor
- Visual template design: See actual layout while editing
- Reusable patterns: Template elements can be copied/modified
- Separation of concerns: Presentation (SVG) vs. logic (JS)
- Easier customization: Change fonts, colors, shapes without touching code
Risks
- Complexity: More complex generator logic
- Performance: Template parsing and cloning overhead
- Compatibility: Need to support both old and new templates
- Testing: More edge cases to test
Final Implementation Summary
Code Changes
-
generator.js: Reduced from 326 to 210 lines (-36%)
- Removed
generateHardcoded()method entirely (~125 lines) - Added
validateTemplate()for strict validation - Simplified
generate()to callgenerateFromTemplates()directly - Updated
extractTemplate()to throw errors instead of returning null
- Removed
-
Templates: Removed old v1 templates
- Deleted
example/template.svg - Deleted
my-project/template.svg - Updated project.json files to reference template-v2.svg exclusively
- Deleted
-
Tests: Refactored for template-only architecture
- engine.test.js: 16 tests kept (unchanged)
- generator.test.js: 23 tests (removed 11 hardcoded tests, kept template tests)
- integration.test.js: 15 comprehensive e2e tests (added 8 new scenarios)
- testHelpers.js: Updated with template-v2 helpers, malformed template generator, large dataset generator
Test Coverage
Unit Tests (39 total):
- 16 engine business logic tests
- 23 generator tests (3 escapeXml, 5 validation, 3 extraction, 4 placeholder, 8 generation)
Integration Tests (15 total):
- Basic e2e workflow
- CSV override handling
- Large dataset (60+ items)
- Date range edge cases (24+ months)
- Special character handling
- Empty CSV handling
- Malformed template error handling (3 tests)
- Template styling preservation
- File upload handling (2 tests)
- Export functionality (2 tests)
Architecture Benefits Achieved
- Single clear path: Template-v2.svg format is required, no fallbacks
- Clear error messages: Immediate feedback when templates are malformed
- Reduced complexity: No branching logic, no backward compatibility code
- Visual editing: Users can edit templates in SVG tools
- Maintainability: ~235 lines of code removed, simpler architecture
Breaking Changes
- Old template.svg format no longer supported
- Must use template-v2.svg with proper
<g id="*-template">elements - No silent fallbacks - errors thrown immediately
Migration Notes
All existing projects already had template-v2.svg files created during Phase 1, so migration was seamless with only project.json updates needed.