# Widget Plugin Infrastructure Workplan

**Document Version**: 1.0
**Date**: 2025-11-10
**Author**: Claude Code
**Objective**: Transform Markitect's UI widgets into a plugin-based architecture with lazy loading and class hierarchy

## Executive Summary

This workplan outlines the transformation of Markitect's current partially-modular JavaScript widget system into a comprehensive plugin infrastructure. The goal is to achieve:

- **Plugin-based architecture** with lazy loading capabilities
- **Class hierarchy with mixin support** to minimize code duplication
- **Isolation between widgets** to prevent breaking changes
- **Performance optimization** through code splitting and dynamic loading
- **Developer productivity** enhancements with hot reloading

## UI Positioning Convention

**Established Convention** for UI controls positioning:

### View Mode Controls
- **Position**: Left border (`left: 20px`)
- **Purpose**: Navigation and document interaction when viewing
- **Examples**: DocumentNavigator, scroll indicators
- **Appearance**: Closed floating elements initially

### Edit Mode Controls
- **Position**: Right border (`right: 20px`)
- **Purpose**: Editor-specific functionality and document manipulation
- **Examples**: Document controls (save, reset), debug panel
- **Appearance**: Closed floating elements initially

### Content-Anchored Controls
- **Position**: Relative to their content location
- **Purpose**: Direct editing of specific content sections
- **Examples**: Section editors, image editors, inline dialogs
- **Appearance**: Positioned contextually near the content they affect

This convention ensures clear separation between viewing and editing interfaces while maintaining consistency across all modes.

## Current State Analysis

### ✅ **What's Working**
- Widgets are partially separated into individual files:
  - `js/components/document-controls.js` - DocumentControls class
  - `js/components/debug-panel.js` - DebugPanel class
  - `js/components/dom-renderer.js` - DOMRenderer class + FloatingMenu
  - `js/core/section-manager.js` - SectionManager class
- Comprehensive TDD test suite exists
- Modular directory structure established

### ❌ **Current Issues**
1. **No ES6 modules**: No import/export system, using global classes
2. **Tight coupling**: Components directly instantiate each other (`new DOMRenderer()`)
3. **No inheritance hierarchy**: All classes are standalone
4. **Embedded dependencies**: FloatingMenu is inside DOMRenderer
5. **No plugin system**: Static loading, no lazy initialization
6. **Manual dependency management**: No automatic loading/injection
7. **Legacy monolith**: 5000+ lines still in `static/editor.js`

## Architecture Overview

### **Target Architecture**

```
markitect/static/js/
├── core/                    # Core plugin system
│   ├── plugin-system.js     # Main plugin registry and loader
│   ├── module-loader.js     # Dynamic module loading
│   ├── dependency-resolver.js # Dependency graph management
│   └── hot-reload.js        # Development hot reloading
├── widgets/                 # Widget class hierarchy
│   ├── base/                # Base widget classes
│   │   ├── Widget.js        # Root widget class
│   │   ├── UIWidget.js      # UI-enabled widget
│   │   └── InteractiveWidget.js # Interactive widget
│   ├── panels/              # Panel-type widgets
│   │   ├── FloatingPanel.js # Base floating panel
│   │   ├── ControlPanel.js  # Document controls
│   │   └── DebugPanel.js    # Debug display
│   ├── editors/             # Editor-type widgets
│   │   ├── Editor.js        # Base editor
│   │   ├── TextEditor.js    # Markdown text editor
│   │   └── ImageEditor.js   # Image editor with drag/drop
│   └── menus/               # Menu-type widgets
│       ├── Menu.js          # Base menu
│       ├── FloatingMenu.js  # Floating context menu
│       └── ContextMenu.js   # Right-click menu
├── mixins/                  # Reusable functionality
│   ├── Draggable.js        # Drag & drop behavior
│   ├── Resizable.js        # Resize handles
│   ├── Themeable.js        # Theme application
│   └── Focusable.js        # Focus management
├── plugins/                 # Plugin definitions
│   ├── document-controls-plugin.js
│   ├── debug-panel-plugin.js
│   ├── text-editor-plugin.js
│   └── image-editor-plugin.js
├── config/                  # Configuration
│   ├── widget-registry.js   # Widget registration
│   └── plugin-config.js     # Default configurations
└── compat/                  # Legacy compatibility
    ├── legacy-bridge.js     # Backward compatibility
    └── migration-helpers.js # Migration utilities
```

## Implementation Plan

### **Phase 1: Foundation Infrastructure** (2-3 days)

#### **1.1 Core Plugin System**

**File**: `js/core/plugin-system.js`
```javascript
class PluginRegistry {
    constructor() {
        this.plugins = new Map();
        this.dependencies = new Map();
        this.loaded = new Set();
        this.instances = new Map();
    }

    async register(name, pluginDefinition) {
        // Register plugin with metadata and dependencies
        this.plugins.set(name, pluginDefinition);
        this.dependencies.set(name, pluginDefinition.dependencies || []);
    }

    async load(name, options = {}) {
        // Lazy load with dependency resolution
        if (this.loaded.has(name)) {
            return this.createInstance(name, options);
        }

        await this.resolveDependencies(name);
        const plugin = await this.loadPlugin(name);
        this.loaded.add(name);

        return this.createInstance(name, options);
    }

    async unload(name) {
        // Clean unloading with dependency checking
    }
}
```

#### **1.2 Base Widget Classes**

**File**: `js/widgets/base/Widget.js`
```javascript
export class Widget extends EventTarget {
    constructor(options = {}) {
        super();
        this.id = options.id || `widget-${Date.now()}`;
        this.container = options.container;
        this.state = new Map();
        this.mixins = [];
        this.config = { ...this.getDefaultConfig(), ...options };
    }

    // Lifecycle methods
    async initialize() { /* Abstract */ }
    async destroy() { /* Cleanup resources */ }

    // State management
    setState(key, value) { /* State updates with events */ }
    getState(key) { /* State retrieval */ }

    // Event management
    emit(event, data) { /* Custom event emission */ }

    // Mixin support
    applyMixin(mixin) { /* Apply mixin functionality */ }

    getDefaultConfig() { return {}; }
}
```

**File**: `js/widgets/base/UIWidget.js`
```javascript
export class UIWidget extends Widget {
    constructor(options) {
        super(options);
        this.element = null;
        this.isVisible = false;
        this.theme = options.theme || 'default';
        this.cssClasses = new Set();
    }

    // UI lifecycle
    async render() { /* Abstract - must implement */ }
    async show() { /* Show widget */ }
    async hide() { /* Hide widget */ }
    async destroy() { /* Cleanup DOM */ }

    // CSS and theming
    addClass(className) { /* Add CSS class */ }
    removeClass(className) { /* Remove CSS class */ }
    applyTheme(theme) { /* Apply theme styling */ }

    // DOM helpers
    createElement(tag, options = {}) { /* Create styled element */ }
    findElement(selector) { /* Find child element */ }
}
```

**File**: `js/widgets/base/InteractiveWidget.js`
```javascript
export class InteractiveWidget extends UIWidget {
    constructor(options) {
        super(options);
        this.handlers = new Map();
        this.shortcuts = new Map();
        this.isInteractionEnabled = true;
    }

    // Event handling
    bindEvent(element, event, handler, options = {}) { /* Bind with cleanup */ }
    unbindEvent(element, event, handler) { /* Clean unbinding */ }
    bindShortcut(keys, handler) { /* Keyboard shortcuts */ }

    // Interaction states
    enable() { /* Enable interactions */ }
    disable() { /* Disable interactions */ }

    // Focus management
    focus() { /* Focus widget */ }
    blur() { /* Blur widget */ }
}
```

#### **1.3 Mixin System**

**File**: `js/mixins/Draggable.js`
```javascript
export const Draggable = {
    makeDraggable(options = {}) {
        this.dragConfig = {
            handle: options.handle || this.element,
            constraint: options.constraint,
            onStart: options.onStart,
            onDrag: options.onDrag,
            onEnd: options.onEnd
        };

        this.bindEvent(this.dragConfig.handle, 'mousedown', this.onDragStart);
        this.isDraggable = true;
    },

    onDragStart(e) {
        if (!this.isDraggable) return;

        this.dragState = {
            startX: e.clientX,
            startY: e.clientY,
            initialX: parseInt(this.element.style.left) || 0,
            initialY: parseInt(this.element.style.top) || 0
        };

        document.addEventListener('mousemove', this.onDrag);
        document.addEventListener('mouseup', this.onDragEnd);

        if (this.dragConfig.onStart) {
            this.dragConfig.onStart.call(this, e);
        }
    },

    onDrag(e) { /* Drag implementation */ },
    onDragEnd(e) { /* End drag implementation */ }
};
```

**File**: `js/mixins/Resizable.js`
```javascript
export const Resizable = {
    makeResizable(options = {}) {
        this.resizeConfig = {
            handles: options.handles || ['se'], // southeast by default
            minWidth: options.minWidth || 100,
            minHeight: options.minHeight || 100,
            onResize: options.onResize
        };

        this.createResizeHandles();
        this.isResizable = true;
    },

    createResizeHandles() { /* Create resize handle elements */ },
    onResizeStart(e) { /* Start resize */ },
    onResize(e) { /* During resize */ },
    onResizeEnd(e) { /* End resize */ }
};
```

**File**: `js/mixins/Themeable.js`
```javascript
export const Themeable = {
    applyTheme(themeName) {
        this.currentTheme = themeName;
        const themeCSS = this.getThemeCSS(themeName);
        this.updateStylesheet(themeCSS);
        this.emit('theme-changed', { theme: themeName });
    },

    getThemeCSS(theme) {
        // Return theme-specific CSS
        const themes = {
            default: { /* default styles */ },
            dark: { /* dark theme styles */ },
            light: { /* light theme styles */ }
        };

        return themes[theme] || themes.default;
    },

    updateStylesheet(styles) { /* Apply styles to widget */ }
};
```

### **Phase 2: Widget Hierarchy Design** (1-2 days)

#### **2.1 Panel Widgets**

**File**: `js/widgets/panels/FloatingPanel.js`
```javascript
import { InteractiveWidget } from '../base/InteractiveWidget.js';
import { Draggable } from '../../mixins/Draggable.js';
import { Themeable } from '../../mixins/Themeable.js';

export class FloatingPanel extends InteractiveWidget {
    constructor(options) {
        super(options);
        this.position = options.position || { top: 20, right: 20 };
        this.isFloating = true;

        // Apply mixins
        this.applyMixin(Draggable);
        this.applyMixin(Themeable);
    }

    async render() {
        this.element = this.createElement('div', {
            className: 'floating-panel',
            style: {
                position: 'fixed',
                top: `${this.position.top}px`,
                right: `${this.position.right}px`,
                zIndex: 1000,
                borderRadius: '8px',
                boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
                backdropFilter: 'blur(8px)'
            }
        });

        this.createHeader();
        this.createBody();
        this.createFooter();

        // Make draggable
        this.makeDraggable({ handle: this.header });

        document.body.appendChild(this.element);
        return this.element;
    }

    createHeader() { /* Header with title and controls */ }
    createBody() { /* Main content area */ }
    createFooter() { /* Optional footer */ }
}
```

**File**: `js/widgets/panels/ControlPanel.js`
```javascript
import { FloatingPanel } from './FloatingPanel.js';

export class ControlPanel extends FloatingPanel {
    constructor(options) {
        super(options);
        this.buttons = new Map();
    }

    getDefaultConfig() {
        return {
            title: 'Document Controls',
            position: { top: 20, right: 20 },
            buttons: [
                { id: 'save', text: '💾 Save Document', color: '#28a745' },
                { id: 'reset', text: '🔄 Reset All', color: '#ffc107' },
                { id: 'status', text: '📊 Show Status', color: '#17a2b8' },
                { id: 'debug', text: '🔍 Debug', color: '#6c757d' }
            ]
        };
    }

    createBody() {
        this.body = this.createElement('div', {
            className: 'control-panel-body'
        });

        this.config.buttons.forEach(buttonConfig => {
            this.addButton(buttonConfig);
        });

        this.element.appendChild(this.body);
    }

    addButton(config) {
        const button = this.createElement('button', {
            textContent: config.text,
            className: 'control-button',
            style: { backgroundColor: config.color }
        });

        this.bindEvent(button, 'click', () => {
            this.emit('button-click', { id: config.id });
        });

        this.buttons.set(config.id, button);
        this.body.appendChild(button);

        return button;
    }
}
```

#### **2.2 Editor Widgets**

**File**: `js/widgets/editors/Editor.js`
```javascript
import { InteractiveWidget } from '../base/InteractiveWidget.js';

export class Editor extends InteractiveWidget {
    constructor(options) {
        super(options);
        this.content = options.content || '';
        this.originalContent = this.content;
        this.isEditing = false;
        this.isDirty = false;
    }

    // Content management
    setContent(content) {
        this.content = content;
        this.isDirty = (content !== this.originalContent);
        this.emit('content-changed', { content, isDirty: this.isDirty });
    }

    getContent() {
        return this.content;
    }

    // Edit lifecycle
    async startEditing() {
        this.isEditing = true;
        this.emit('edit-start');
    }

    async stopEditing(save = false) {
        if (save) {
            this.originalContent = this.content;
            this.isDirty = false;
        } else {
            this.content = this.originalContent;
        }

        this.isEditing = false;
        this.emit('edit-end', { saved: save });
    }

    // Abstract methods
    async render() { /* Must implement */ }
    focus() { /* Must implement */ }
}
```

**File**: `js/widgets/editors/TextEditor.js`
```javascript
import { Editor } from './Editor.js';

export class TextEditor extends Editor {
    constructor(options) {
        super(options);
        this.autoResize = options.autoResize !== false;
        this.placeholder = options.placeholder || 'Enter text...';
    }

    async render() {
        this.element = this.createElement('div', {
            className: 'text-editor'
        });

        this.textarea = this.createElement('textarea', {
            value: this.content,
            placeholder: this.placeholder,
            className: 'text-editor-input'
        });

        this.bindEvent(this.textarea, 'input', this.onInput);
        this.bindEvent(this.textarea, 'keydown', this.onKeyDown);

        if (this.autoResize) {
            this.setupAutoResize();
        }

        this.createToolbar();
        this.element.appendChild(this.textarea);

        return this.element;
    }

    onInput(e) {
        this.setContent(e.target.value);
        if (this.autoResize) {
            this.resizeToContent();
        }
    }

    onKeyDown(e) {
        // Handle shortcuts (Ctrl+Enter, Escape, etc.)
        if (e.ctrlKey && e.key === 'Enter') {
            this.stopEditing(true);
        } else if (e.key === 'Escape') {
            this.stopEditing(false);
        }
    }

    setupAutoResize() { /* Auto-resize textarea logic */ }
    createToolbar() { /* Editor toolbar */ }
    focus() { this.textarea.focus(); }
}
```

### **Phase 3: Lazy Loading System** (2 days)

#### **3.1 Module Loader**

**File**: `js/core/module-loader.js`
```javascript
export class ModuleLoader {
    constructor() {
        this.cache = new Map();
        this.loading = new Map();
        this.registry = new Map();
    }

    register(name, importFn) {
        this.registry.set(name, importFn);
    }

    async loadWidget(widgetName, options = {}) {
        // Check cache first
        if (this.cache.has(widgetName)) {
            return this.createInstance(widgetName, options);
        }

        // Check if already loading
        if (this.loading.has(widgetName)) {
            await this.loading.get(widgetName);
            return this.createInstance(widgetName, options);
        }

        // Start loading
        const loadPromise = this.loadModule(widgetName);
        this.loading.set(widgetName, loadPromise);

        try {
            const module = await loadPromise;
            this.cache.set(widgetName, module);
            this.loading.delete(widgetName);
            return this.createInstance(widgetName, options);
        } catch (error) {
            this.loading.delete(widgetName);
            throw new Error(`Failed to load widget '${widgetName}': ${error.message}`);
        }
    }

    async loadModule(widgetName) {
        const importFn = this.registry.get(widgetName);
        if (!importFn) {
            throw new Error(`Widget '${widgetName}' not registered`);
        }

        const module = await importFn();
        return module.default || module;
    }

    createInstance(widgetName, options) {
        const WidgetClass = this.cache.get(widgetName);
        return new WidgetClass(options);
    }

    unload(widgetName) {
        this.cache.delete(widgetName);
        this.loading.delete(widgetName);
    }
}
```

#### **3.2 Dependency Resolution**

**File**: `js/core/dependency-resolver.js`
```javascript
export class DependencyResolver {
    constructor(moduleLoader) {
        this.moduleLoader = moduleLoader;
        this.dependencyGraph = new Map();
        this.resolved = new Set();
    }

    registerDependencies(widgetName, dependencies) {
        this.dependencyGraph.set(widgetName, dependencies || []);
    }

    async resolveDependencies(widgetName, visited = new Set()) {
        // Circular dependency detection
        if (visited.has(widgetName)) {
            throw new Error(`Circular dependency detected: ${Array.from(visited).join(' -> ')} -> ${widgetName}`);
        }

        // Already resolved
        if (this.resolved.has(widgetName)) {
            return [];
        }

        visited.add(widgetName);
        const dependencies = this.dependencyGraph.get(widgetName) || [];
        const resolved = [];

        // Resolve each dependency recursively
        for (const dep of dependencies) {
            const depResolved = await this.resolveDependencies(dep, visited);
            resolved.push(...depResolved);

            if (!this.resolved.has(dep)) {
                const depInstance = await this.moduleLoader.loadWidget(dep);
                resolved.push(depInstance);
                this.resolved.add(dep);
            }
        }

        visited.delete(widgetName);
        return resolved;
    }

    getDependencyOrder(widgetName) {
        // Returns topologically sorted dependency order
        const visited = new Set();
        const order = [];

        const visit = (name) => {
            if (visited.has(name)) return;
            visited.add(name);

            const deps = this.dependencyGraph.get(name) || [];
            deps.forEach(visit);
            order.push(name);
        };

        visit(widgetName);
        return order;
    }
}
```

### **Phase 4: Plugin Definitions** (1-2 days)

#### **4.1 Widget Plugin Registration**

**File**: `js/plugins/document-controls-plugin.js`
```javascript
export default {
    name: 'DocumentControls',
    version: '1.0.0',
    description: 'Document control panel with save, reset, and status functions',
    author: 'Markitect Core',

    // Dependencies that must be loaded first
    dependencies: ['FloatingPanel', 'Button'],

    // Mixins to apply
    mixins: ['Draggable', 'Themeable', 'Focusable'],

    // Lazy load the actual widget class
    async load() {
        const { ControlPanel } = await import('../widgets/panels/ControlPanel.js');
        return ControlPanel;
    },

    // Default configuration
    defaultOptions: {
        position: { top: 20, right: 20 },
        theme: 'default',
        draggable: true,
        title: 'Document Controls',
        buttons: [
            { id: 'save-document', text: '💾 Save Document', color: '#28a745' },
            { id: 'reset-all', text: '🔄 Reset All', color: '#ffc107' },
            { id: 'show-status', text: '📊 Show Status', color: '#17a2b8' },
            { id: 'toggle-debug', text: '🔍 Debug', color: '#6c757d' }
        ]
    },

    // Plugin lifecycle hooks
    async onLoad(instance, options) {
        // Called after widget is loaded and created
        console.log(`DocumentControls plugin loaded with options:`, options);
    },

    async onUnload(instance) {
        // Called before widget is destroyed
        console.log('DocumentControls plugin unloading');
    },

    // Feature flags and capabilities
    capabilities: {
        draggable: true,
        resizable: false,
        themeable: true,
        persistent: true // Survives page reloads
    }
};
```

#### **4.2 Widget Registry Configuration**

**File**: `js/config/widget-registry.js`
```javascript
export const widgetRegistry = {
    // Core widgets
    'DocumentControls': () => import('../plugins/document-controls-plugin.js'),
    'DebugPanel': () => import('../plugins/debug-panel-plugin.js'),
    'StatusModal': () => import('../plugins/status-modal-plugin.js'),

    // Editor widgets
    'TextEditor': () => import('../plugins/text-editor-plugin.js'),
    'ImageEditor': () => import('../plugins/image-editor-plugin.js'),
    'MarkdownEditor': () => import('../plugins/markdown-editor-plugin.js'),

    // Menu widgets
    'FloatingMenu': () => import('../plugins/floating-menu-plugin.js'),
    'ContextMenu': () => import('../plugins/context-menu-plugin.js'),
    'DropdownMenu': () => import('../plugins/dropdown-menu-plugin.js'),

    // Utility widgets
    'LoadingSpinner': () => import('../plugins/loading-spinner-plugin.js'),
    'ProgressBar': () => import('../plugins/progress-bar-plugin.js'),
    'Toast': () => import('../plugins/toast-plugin.js'),
};

export const pluginCategories = {
    'panels': ['DocumentControls', 'DebugPanel'],
    'editors': ['TextEditor', 'ImageEditor', 'MarkdownEditor'],
    'menus': ['FloatingMenu', 'ContextMenu', 'DropdownMenu'],
    'utilities': ['LoadingSpinner', 'ProgressBar', 'Toast']
};

export const dependencyGraph = {
    'DocumentControls': ['FloatingPanel'],
    'DebugPanel': ['FloatingPanel'],
    'TextEditor': ['Editor'],
    'ImageEditor': ['Editor'],
    'FloatingMenu': ['Menu'],
    'ContextMenu': ['Menu']
};
```

#### **4.3 Main Plugin System Integration**

**File**: `js/core/widget-system.js`
```javascript
import { PluginRegistry } from './plugin-system.js';
import { ModuleLoader } from './module-loader.js';
import { DependencyResolver } from './dependency-resolver.js';
import { widgetRegistry, dependencyGraph } from '../config/widget-registry.js';

export class WidgetSystem {
    constructor() {
        this.moduleLoader = new ModuleLoader();
        this.dependencyResolver = new DependencyResolver(this.moduleLoader);
        this.pluginRegistry = new PluginRegistry();
        this.activeWidgets = new Map();

        this.initialize();
    }

    async initialize() {
        // Register all widgets from registry
        for (const [name, importFn] of Object.entries(widgetRegistry)) {
            this.moduleLoader.register(name, importFn);
            this.dependencyResolver.registerDependencies(name, dependencyGraph[name]);
        }
    }

    async createWidget(widgetName, options = {}) {
        try {
            // Resolve dependencies first
            await this.dependencyResolver.resolveDependencies(widgetName);

            // Load the widget
            const widget = await this.moduleLoader.loadWidget(widgetName, options);

            // Track active widgets
            const id = widget.id;
            this.activeWidgets.set(id, widget);

            // Setup cleanup
            widget.addEventListener('destroy', () => {
                this.activeWidgets.delete(id);
            });

            return widget;
        } catch (error) {
            console.error(`Failed to create widget '${widgetName}':`, error);
            throw error;
        }
    }

    async destroyWidget(widgetId) {
        const widget = this.activeWidgets.get(widgetId);
        if (widget) {
            await widget.destroy();
            this.activeWidgets.delete(widgetId);
        }
    }

    getActiveWidgets() {
        return Array.from(this.activeWidgets.values());
    }

    getWidget(widgetId) {
        return this.activeWidgets.get(widgetId);
    }
}

// Global widget system instance
export const widgetSystem = new WidgetSystem();
```

### **Phase 5: Migration Strategy** (2-3 days)

#### **5.1 Backward Compatibility Layer**

**File**: `js/compat/legacy-bridge.js`
```javascript
import { widgetSystem } from '../core/widget-system.js';

/**
 * Legacy compatibility bridge for existing code
 * Provides old-style constructor access during migration
 */
export class LegacyBridge {
    constructor() {
        this.legacyInstances = new Map();
        this.setupGlobalConstructors();
    }

    setupGlobalConstructors() {
        // Provide legacy global constructors
        window.DocumentControls = this.createLegacyConstructor('DocumentControls');
        window.DebugPanel = this.createLegacyConstructor('DebugPanel');
        window.DOMRenderer = this.createLegacyConstructor('DOMRenderer');
        window.SectionManager = this.createLegacyConstructor('SectionManager');
    }

    createLegacyConstructor(widgetName) {
        return async function(options = {}) {
            console.warn(`Using legacy constructor for ${widgetName}. Consider migrating to widget system.`);

            const widget = await widgetSystem.createWidget(widgetName, options);

            // Store legacy reference
            this.legacyInstances.set(widget.id, widget);

            return widget;
        }.bind(this);
    }

    async migrate(legacyInstance) {
        // Helper to migrate existing instances
        const widgetName = legacyInstance.constructor.name;
        const options = this.extractOptions(legacyInstance);

        const newWidget = await widgetSystem.createWidget(widgetName, options);

        // Transfer state
        this.transferState(legacyInstance, newWidget);

        return newWidget;
    }

    extractOptions(legacyInstance) {
        // Extract configuration from legacy instance
        return {
            container: legacyInstance.container,
            theme: legacyInstance.theme,
            // ... other extractable options
        };
    }

    transferState(from, to) {
        // Transfer state between instances
        if (from.isVisible) {
            to.show();
        }
        // ... other state transfers
    }
}
```

#### **5.2 Migration Plan**

**Phase 5a: Setup (Day 1)**
1. Add new plugin infrastructure alongside existing code
2. Set up legacy bridge for backward compatibility
3. Update build process to include new modules
4. Create migration testing framework

**Phase 5b: Widget-by-Widget Migration (Days 2-3)**

Migration order (least dependent first):
1. **DebugPanel** → Migrate first (no dependencies)
2. **DocumentControls** → Migrate second (minimal dependencies)
3. **FloatingMenu** → Extract from DOMRenderer
4. **TextEditor/ImageEditor** → Migrate edit functionality
5. **DOMRenderer** → Migrate remaining functionality
6. **SectionManager** → Migrate core logic

For each widget:
1. Create plugin definition
2. Migrate widget to new base classes
3. Update tests to use new system
4. Add legacy compatibility shim
5. Verify existing functionality works

**Phase 5c: Testing and Validation (Throughout)**
1. Run existing test suite with legacy bridge
2. Add new plugin-specific tests
3. Test hot reloading functionality
4. Performance testing with lazy loading

**Phase 5d: Cleanup (After migration)**
1. Remove legacy bridge once all code migrated
2. Remove old monolithic files
3. Update documentation
4. Performance optimization

### **Phase 6: Advanced Features** (2 days)

#### **6.1 Hot Reloading for Development**

**File**: `js/core/hot-reload.js`
```javascript
export class HotReloader {
    constructor(widgetSystem) {
        this.widgetSystem = widgetSystem;
        this.watchedModules = new Map();
        this.stateSnapshots = new Map();

        if (process.env.NODE_ENV === 'development') {
            this.enableHotReload();
        }
    }

    enableHotReload() {
        // Listen for module update events
        if (module.hot) {
            module.hot.accept();
            module.hot.addStatusHandler(status => {
                if (status === 'idle') {
                    this.onModuleUpdate();
                }
            });
        }
    }

    async reloadWidget(widgetName) {
        console.log(`Hot reloading widget: ${widgetName}`);

        // Find all active instances of this widget
        const instances = this.findActiveInstances(widgetName);

        // Save state snapshots
        for (const instance of instances) {
            this.stateSnapshots.set(instance.id, this.captureState(instance));
        }

        // Unload old module
        this.widgetSystem.moduleLoader.unload(widgetName);

        // Reload module
        const newInstances = [];
        for (const instance of instances) {
            const snapshot = this.stateSnapshots.get(instance.id);

            // Destroy old instance
            await instance.destroy();

            // Create new instance
            const newInstance = await this.widgetSystem.createWidget(widgetName, snapshot.options);

            // Restore state
            this.restoreState(newInstance, snapshot.state);

            newInstances.push(newInstance);
        }

        console.log(`Hot reload complete for ${widgetName}: ${newInstances.length} instances updated`);
        return newInstances;
    }

    findActiveInstances(widgetName) {
        return this.widgetSystem.getActiveWidgets()
            .filter(widget => widget.constructor.name === widgetName);
    }

    captureState(widget) {
        return {
            options: widget.config,
            state: {
                isVisible: widget.isVisible,
                position: widget.position,
                content: widget.content,
                customState: widget.state ? Object.fromEntries(widget.state) : {}
            }
        };
    }

    restoreState(widget, state) {
        widget.isVisible = state.isVisible;
        widget.position = state.position;
        if (state.content) widget.setContent(state.content);

        // Restore custom state
        for (const [key, value] of Object.entries(state.customState)) {
            widget.setState(key, value);
        }

        // Show if was visible
        if (state.isVisible) {
            widget.show();
        }
    }
}
```

#### **6.2 Plugin Development Tools**

**File**: `js/dev/plugin-dev-tools.js`
```javascript
export class PluginDevTools {
    constructor(widgetSystem) {
        this.widgetSystem = widgetSystem;
        this.createDevPanel();
    }

    createDevPanel() {
        if (process.env.NODE_ENV !== 'development') return;

        const devPanel = document.createElement('div');
        devPanel.id = 'plugin-dev-tools';
        devPanel.innerHTML = `
            <div class="dev-panel-header">Plugin Dev Tools</div>
            <div class="dev-panel-body">
                <button id="reload-all">Reload All Widgets</button>
                <button id="inspect-widgets">Inspect Active Widgets</button>
                <button id="performance-stats">Performance Stats</button>
                <div id="widget-list"></div>
            </div>
        `;

        this.styleDev Panel(devPanel);
        document.body.appendChild(devPanel);
        this.bindDevPanelEvents(devPanel);
    }

    async reloadAllWidgets() {
        const widgets = this.widgetSystem.getActiveWidgets();
        const results = [];

        for (const widget of widgets) {
            try {
                const newWidget = await this.hotReloader.reloadWidget(widget.constructor.name);
                results.push({ widget: widget.constructor.name, status: 'success' });
            } catch (error) {
                results.push({ widget: widget.constructor.name, status: 'error', error });
            }
        }

        console.table(results);
        return results;
    }

    inspectActiveWidgets() {
        const widgets = this.widgetSystem.getActiveWidgets();
        const inspection = widgets.map(widget => ({
            id: widget.id,
            type: widget.constructor.name,
            visible: widget.isVisible,
            state: Object.fromEntries(widget.state || [])
        }));

        console.table(inspection);
        return inspection;
    }
}
```

## Testing Strategy

### **Unit Testing**
Each widget will have comprehensive unit tests:

```javascript
// js/tests/widgets/ControlPanel.test.js
import { ControlPanel } from '../widgets/panels/ControlPanel.js';

describe('ControlPanel', () => {
    let controlPanel;

    beforeEach(() => {
        controlPanel = new ControlPanel({
            position: { top: 100, right: 100 }
        });
    });

    afterEach(async () => {
        if (controlPanel) {
            await controlPanel.destroy();
        }
    });

    test('should create with default configuration', () => {
        expect(controlPanel.config.title).toBe('Document Controls');
        expect(controlPanel.config.buttons).toHaveLength(4);
    });

    test('should render floating panel', async () => {
        const element = await controlPanel.render();
        expect(element).toBeInstanceOf(HTMLElement);
        expect(element.classList.contains('floating-panel')).toBe(true);
    });

    test('should emit button-click events', async () => {
        await controlPanel.render();
        const eventSpy = jest.fn();
        controlPanel.addEventListener('button-click', eventSpy);

        const saveButton = controlPanel.buttons.get('save');
        saveButton.click();

        expect(eventSpy).toHaveBeenCalledWith(
            expect.objectContaining({
                detail: { id: 'save' }
            })
        );
    });
});
```

### **Integration Testing**
Test widget interactions and plugin loading:

```javascript
// js/tests/integration/plugin-system.test.js
import { widgetSystem } from '../core/widget-system.js';

describe('Plugin System Integration', () => {
    test('should load widget with dependencies', async () => {
        const controlPanel = await widgetSystem.createWidget('DocumentControls');

        expect(controlPanel).toBeDefined();
        expect(controlPanel.constructor.name).toBe('ControlPanel');
    });

    test('should handle circular dependencies', async () => {
        await expect(
            widgetSystem.createWidget('CircularWidget')
        ).rejects.toThrow('Circular dependency detected');
    });
});
```

## Performance Considerations

### **Bundle Optimization**
- **Code splitting**: Each widget is a separate chunk
- **Tree shaking**: Only load used functionality
- **Lazy loading**: Load widgets on demand
- **Caching**: Module-level caching for repeated loads

### **Memory Management**
- **Proper cleanup**: Widgets clean up event listeners and DOM
- **State management**: Efficient state storage and updates
- **Memory leaks**: Comprehensive testing for leaks

### **Loading Performance**
- **Preloading**: Predictive loading of likely-needed widgets
- **Chunking**: Optimal chunk sizing for network requests
- **Compression**: Gzipped module delivery

## Benefits Summary

### ✅ **Isolation & Stability**
- **Independent updates**: Each widget can be updated without affecting others
- **Failure isolation**: Widget crashes don't bring down the entire system
- **Version management**: Individual widget versioning and rollback capability

### ✅ **Performance**
- **Lazy loading**: 60-80% reduction in initial bundle size
- **Code splitting**: Faster page loads and better caching
- **Memory efficiency**: Unload unused widgets to free memory

### ✅ **Code Reuse**
- **Mixin system**: Share common functionality across widgets
- **Base classes**: Inherit common widget behaviors
- **Composition**: Flexible widget construction and customization

### ✅ **Developer Experience**
- **Hot reloading**: Instant feedback during development
- **Plugin discovery**: Easy exploration of available widgets
- **Testing isolation**: Test widgets independently with mocked dependencies
- **Type safety**: Full TypeScript support potential

## Risk Mitigation

### **Migration Risks**
- **Backward compatibility**: Legacy bridge ensures existing code works
- **Gradual migration**: Widget-by-widget migration minimizes risk
- **Comprehensive testing**: Existing test suite validates functionality

### **Performance Risks**
- **Bundle size**: Code splitting prevents size increases
- **Loading delays**: Preloading and caching minimize delays
- **Memory usage**: Proper cleanup prevents memory leaks

### **Complexity Risks**
- **Learning curve**: Comprehensive documentation and examples
- **Debugging**: Development tools and clear error messages
- **Maintenance**: Clean architecture reduces maintenance burden

## Estimated Timeline

| Phase | Duration | Deliverables |
|-------|----------|-------------|
| Phase 1: Foundation | 2-3 days | Core plugin system, base classes, mixins |
| Phase 2: Widget Hierarchy | 1-2 days | Panel, editor, menu widget classes |
| Phase 3: Lazy Loading | 2 days | Module loader, dependency resolver |
| Phase 4: Plugin Definitions | 1-2 days | Plugin configurations, widget registry |
| Phase 5: Migration | 2-3 days | Legacy bridge, widget-by-widget migration |
| Phase 6: Advanced Features | 2 days | Hot reloading, dev tools |
| **Total** | **8-12 days** | **Complete plugin infrastructure** |

## Success Metrics

### **Functional**
- ✅ All existing widgets work without modification
- ✅ New widgets can be added without touching existing code
- ✅ Hot reloading works for all widgets
- ✅ Plugin loading time < 100ms per widget

### **Performance**
- ✅ 60%+ reduction in initial bundle size
- ✅ <50ms widget instantiation time
- ✅ Zero memory leaks in widget lifecycle
- ✅ 100% test coverage maintained

### **Developer Experience**
- ✅ Widget development takes 50% less time
- ✅ Zero breaking changes during updates
- ✅ Complete TypeScript support
- ✅ Comprehensive documentation and examples

This plugin infrastructure will transform Markitect's UI system into a modern, maintainable, and extensible architecture that supports rapid development while ensuring stability and performance.