TRACE has been refactored from a monolithic architecture to a modular plugin system. This guide explains the changes and how to migrate any customizations.
js/
├── trace-engine.js (1306 lines, everything in one file)
├── theme.js (theme data)
└── app.js (bootstrap)
js/
├── core/
│ ├── trace-engine.js (300 lines, core only)
│ ├── plugin-manager.js (plugin registration)
│ ├── constants.js (shared constants)
│ └── utils.js (utilities)
├── plugins/
│ ├── theme.plugin.js (theme management)
│ ├── locale.plugin.js (i18n)
│ ├── tooltip.plugin.js (tooltips)
│ ├── interaction.plugin.js (gestures)
│ ├── progress.plugin.js (time updates)
│ ├── devtools.plugin.js (dev utilities)
│ └── a11y.plugin.js (accessibility)
├── config/
│ └── theme-colors.js (theme data)
└── app.js (plugin loader)
Before:
import { TraceEngine } from './trace-engine.js';
import { THEME_COLORS, COLOR_OF_YEAR } from './theme.js';After:
import { TraceEngine } from './core/trace-engine.js';
import { THEME_COLORS, COLOR_OF_YEAR } from './config/theme-colors.js';The core TraceEngine class now has a minimal API. Feature-specific methods moved to plugins.
Before:
engine.cycleTheme();
engine.randomizeNowUTC();
engine.setLocale('id');After:
// Access via plugins
const themePlugin = engine.plugins.get('ThemePlugin');
themePlugin.cycleTheme();
const devTools = engine.plugins.get('DevToolsPlugin');
devTools.randomizeNowUTC();
const localePlugin = engine.plugins.get('LocalePlugin');
localePlugin.setLocale('id');If you have custom code importing TRACE modules:
// Old imports
import { TraceEngine } from './js/trace-engine.js';
import { THEME_COLORS } from './js/theme.js';
// New imports
import { TraceEngine } from './js/core/trace-engine.js';
import { THEME_COLORS } from './js/config/theme-colors.js';Replace direct engine method calls with plugin access:
// Old way
engine.cycleTheme();
// New way
const themePlugin = engine.plugins.get('ThemePlugin');
if (themePlugin) {
themePlugin.cycleTheme();
}If you had custom modifications, convert them to plugins:
Before (modifying trace-engine.js):
class TraceEngine {
// ... original code
// Your custom method
myCustomFeature() {
console.log('Custom!');
}
}After (create custom plugin):
// plugins/custom.plugin.js
import { TracePlugin } from '../core/plugin-manager.js';
export class CustomPlugin extends TracePlugin {
constructor() {
super('CustomPlugin');
}
init(engine) {
super.init(engine);
this.myCustomFeature();
}
myCustomFeature() {
console.log('Custom!');
}
}
// app.js
import { CustomPlugin } from './plugins/custom.plugin.js';
engine.plugins.register('CustomPlugin', new CustomPlugin());Create a wrapper to maintain old API:
// js/trace-engine-compat.js
import { TraceEngine as CoreEngine } from './core/trace-engine.js';
export class TraceEngine extends CoreEngine {
cycleTheme() {
return this.plugins.get('ThemePlugin')?.cycleTheme();
}
setLocale(locale, options) {
return this.plugins.get('LocalePlugin')?.setLocale(locale, options);
}
randomizeNowUTC() {
return this.plugins.get('DevToolsPlugin')?.randomizeNowUTC();
}
// Add other legacy methods as needed
}Update all references to use the new plugin API directly.
| Old Method | New Location |
|---|---|
engine.cycleTheme() |
ThemePlugin.cycleTheme() |
engine.setThemeIndex() |
ThemePlugin.setThemeIndex() |
engine.updateDynamicColors() |
ThemePlugin.updateDynamicColors() |
engine.setLocale() |
LocalePlugin.setLocale() |
engine.formatDayInfo() |
LocalePlugin.formatDayInfo() |
engine.showTooltipAt() |
TooltipPlugin.showTooltipAt() |
engine.triggerHaptic() |
InteractionPlugin.triggerHaptic() |
engine.randomizeNowUTC() |
DevToolsPlugin.randomizeNowUTC() |
engine.resetToDefaults() |
DevToolsPlugin.resetToDefaults() |
- Enable/disable features independently
- Load only what you need
- Smaller, focused files
- Clear separation of concerns
- Easier to understand and modify
- Test plugins in isolation
- Mock dependencies easily
- Faster test execution
- Create custom plugins
- Override default behavior
- Lazy-load plugins on demand (future)
- Tree-shake unused code
- Smaller bundle size
// plugins/seasonal-theme.plugin.js
import { TracePlugin } from '../core/plugin-manager.js';
export class SeasonalThemePlugin extends TracePlugin {
constructor() {
super('SeasonalThemePlugin');
}
init(engine) {
super.init(engine);
this.applySeasonalTheme();
}
applySeasonalTheme() {
const month = this.engine.getNow().getMonth();
const themePlugin = this.engine.plugins.get('ThemePlugin');
if (!themePlugin) return;
// Spring: greens
if (month >= 2 && month <= 4) {
themePlugin.setThemeIndex(4);
}
// Summer: warm colors
else if (month >= 5 && month <= 7) {
themePlugin.setThemeIndex(7);
}
// Fall: earth tones
else if (month >= 8 && month <= 10) {
themePlugin.setThemeIndex(6);
}
// Winter: cool colors
else {
themePlugin.setThemeIndex(2);
}
}
}// plugins/analytics.plugin.js
import { TracePlugin } from '../core/plugin-manager.js';
export class AnalyticsPlugin extends TracePlugin {
constructor() {
super('AnalyticsPlugin');
this.sessionStart = Date.now();
}
init(engine) {
super.init(engine);
// Track renders
this.renderCount = 0;
}
onRender() {
this.renderCount++;
console.log('Total renders:', this.renderCount);
}
getSessionDuration() {
return Date.now() - this.sessionStart;
}
}// app.js - Minimal setup without devtools
import { TraceEngine } from './core/trace-engine.js';
import { ThemePlugin } from './plugins/theme.plugin.js';
import { LocalePlugin } from './plugins/locale.plugin.js';
const engine = new TraceEngine({/* config */});
// Register only essential plugins
engine.plugins.register('LocalePlugin', new LocalePlugin());
engine.plugins.register('ThemePlugin', new ThemePlugin());
// Skip DevToolsPlugin, InteractionPlugin, etc.
engine.init();
engine.render();Cause: Plugin not registered or accessed before initialization.
Solution:
// Ensure plugin is registered
const themePlugin = engine.plugins.get('ThemePlugin');
if (!themePlugin) {
console.error('ThemePlugin not registered');
return;
}
themePlugin.cycleTheme();Cause: Incorrect import path.
Solution: Update imports to new paths:
// ❌ Old
import { TraceEngine } from './js/trace-engine.js';
// ✅ New
import { TraceEngine } from './js/core/trace-engine.js';Cause: Plugins not initialized or wrong order.
Solution: Check plugin registration order in app.js:
// Correct order
engine.plugins.register('LocalePlugin', new LocalePlugin()); // 1st
engine.plugins.register('ThemePlugin', new ThemePlugin()); // 2nd
engine.plugins.register('InteractionPlugin', new InteractionPlugin()); // After deps- Check
PLUGIN_SYSTEM.mdfor detailed plugin API documentation - Review
js/plugins/*.plugin.jsfor examples - Open an issue on GitHub with migration questions
If you need to rollback to the old monolithic version:
# Restore old files
cd js/
mv trace-engine.js.old trace-engine.js
mv theme.js.old theme.js
# Update app.js to use old imports
# (restore from git history)The old files are preserved as *.old for safety.