```
class PluginInterface {
  /**
   * Attaches the plugin to the system by setting global CSS variables and returning CSS rules.
   * @param {HTMLElement} host - The shadow host element.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  attach(host) {
    throw new Error('attach method must be implemented by the plugin.');
  }

  /**
   * Detaches the plugin from the system by removing global CSS variables and performing cleanup.
   * @param {HTMLElement} host - The shadow host element.
   */
  detach(host) {
    throw new Error('detach method must be implemented by the plugin.');
  }

  /**
   * Updates global configurations and returns the updated CSS.
   * @param {Object} options - Options to update.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  updateGlobal(options = {}) {
    throw new Error('updateGlobal method must be implemented by the plugin.');
  }

  /**
   * Updates per-rectangle configurations.
   * @param {HTMLElement} rect - The rectangle element.
   * @param {Object} options - Options to update.
   */
  updateRect(rect, options = {}) {
    throw new Error('updateRect method must be implemented by the plugin.');
  }

  /**
   * Cleans up per-rectangle configurations if necessary.
   * @param {HTMLElement} rect - The rectangle element.
   */
  onRectRemove(rect) {
    throw new Error('onRectRemove method must be implemented by the plugin.');
  }
}

// BorderPlugin Implementation
class BorderPlugin extends PluginInterface {
  constructor(options = {}) {
    super();
    this.size = options.size || '2px';
    this.color = options.color || '#000';
    this.host = null;
    this.updateCSS();
  }

  /**
   * Updates the CSS rules based on current configurations.
   */
  updateCSS() {
    this.baseRule = `
      border: var(--border-size, ${this.size}) solid var(--border-color, ${this.color});
    `;
    
    // BorderPlugin doesn't have additional CSS rules
    this.additionalCSS = '';
  }

  /**
   * Attaches the plugin by setting global CSS variables and returning CSS rules.
   * @param {HTMLElement} host - The shadow host element.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  attach(host) {
    if (host) {
      this.host = host;
    }
    host = this.host;
    host.style.setProperty('--border-size', this.size);
    host.style.setProperty('--border-color', this.color);
    this.updateCSS();
    return [this.baseRule, this.additionalCSS];
  }

  /**
   * Detaches the plugin by removing global CSS variables.
   * @param {HTMLElement} host - The shadow host element.
   */
  detach(host) {
    host.style.removeProperty('--border-size');
    host.style.removeProperty('--border-color');
  }

  /**
   * Provides the updated CSS rules for borders.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  getCSS() {
    return [this.baseRule, this.additionalCSS];
  }

  /**
   * Updates global configurations and returns the updated CSS.
   * @param {Object} options - Options to update.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  updateGlobal(options = {}) {
    if (options.size) this.size = options.size;
    if (options.color) this.color = options.color;
    return this.attach();
  }

  /**
   * Updates per-rectangle configurations by setting CSS variables on the rectangle.
   * @param {HTMLElement} rect - The rectangle element.
   * @param {Object} options - Options to update.
   */
  updateRect(rect, options = {}) {
    if (options.size) {
      rect.style.setProperty('--border-size', typeof options.size === 'number' ? `${options.size}px` : options.size);
    }
    if (options.color) {
      rect.style.setProperty('--border-color', options.color);
    }
  }

  /**
   * Cleans up per-rectangle configurations by removing CSS variables.
   * @param {HTMLElement} rect - The rectangle element.
   */
  onRectRemove(rect) {
    rect.style.removeProperty('--border-size');
    rect.style.removeProperty('--border-color');
  }
}

// VertexPlugin Implementation
class VertexPlugin extends PluginInterface {
  constructor(options = {}) {
    super();
    this.size = options.size || '25px';
    this.color = options.color || '#3498db';
    this.host = null;
    this.updateCSS();
  }

  /**
   * Updates the CSS rules based on current configurations.
   */
  updateCSS() {
    // VertexPlugin doesn't have base CSS for .rect-region
    this.baseRule = '';

    this.additionalCSS = `
      .rect-region::before {
        content: '';
        position: absolute;
        width: var(--vertex-size, ${this.size});
        height: var(--vertex-size, ${this.size});
        background-color: var(--vertex-color, ${this.color});
        box-sizing: border-box;

        /* Intermediate variables */
        --offset: calc(-0.5 * (var(--border-size, 2px) + var(--vertex-size)));
        --adjusted-width: calc(var(--width) - var(--border-size, 2px));
        --adjusted-height: calc(var(--height) - var(--border-size, 2px));

        /* Positioning the first vertex */
        top: var(--offset);
        left: var(--offset);

        /* Creating three more vertices using box-shadow, positioned at each corner */
        box-shadow:
          var(--adjusted-width) 0 0 0 var(--vertex-color),
          var(--adjusted-width) var(--adjusted-height) 0 0 var(--vertex-color),
          0 var(--adjusted-height) 0 0 var(--vertex-color);
      }
    `;
  }

  /**
   * Attaches the plugin by setting global CSS variables and returning CSS rules.
   * @param {HTMLElement} host - The shadow host element.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  attach(host) {
    if (host) {
      this.host = host;
    }
    host = this.host;
    host.style.setProperty('--vertex-size', this.size);
    host.style.setProperty('--vertex-color', this.color);
    this.updateCSS();
    return [this.baseRule, this.additionalCSS];
  }

  /**
   * Detaches the plugin by removing global CSS variables.
   * @param {HTMLElement} host - The shadow host element.
   */
  detach(host) {
    host.style.removeProperty('--vertex-size');
    host.style.removeProperty('--vertex-color');
  }

  /**
   * Provides the updated CSS rules for vertices.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  getCSS() {
    return [this.baseRule, this.additionalCSS];
  }

  /**
   * Updates global configurations and returns the updated CSS.
   * @param {Object} options - Options to update.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  updateGlobal(options = {}) {
    if (options.size) this.size = options.size;
    if (options.color) this.color = options.color;
    return this.attach();
  }

  /**
   * Updates per-rectangle configurations by setting CSS variables on the rectangle.
   * @param {HTMLElement} rect - The rectangle element.
   * @param {Object} options - Options to update.
   */
  updateRect(rect, options = {}) {
    if (options.size) {
      rect.style.setProperty('--vertex-size', typeof options.size === 'number' ? `${options.size}px` : options.size);
    }
    if (options.color) {
      rect.style.setProperty('--vertex-color', options.color);
    }
  }

  /**
   * Cleans up per-rectangle configurations by removing CSS variables.
   * @param {HTMLElement} rect - The rectangle element.
   */
  onRectRemove(rect) {
    rect.style.removeProperty('--vertex-size');
    rect.style.removeProperty('--vertex-color');
  }
}

class FillPlugin extends PluginInterface {
  /**
   * Constructs a FillPlugin instance with given options.
   * @param {Object} options - Configuration options.
   * @param {string} options.color - The fill color (e.g., '#ff0000').
   */
  constructor(options = {}) {
    super();
    this.color = options.color || '#ffffff'; // Default fill color is white
    this.host = null;
    this.updateCSS();
  }

  /**
   * Updates the CSS rules based on current configurations.
   */
  updateCSS() {
    this.baseRule = `
      background-color: var(--fill-color, ${this.color});
    `;

    // FillPlugin doesn't have additional CSS rules
    this.additionalCSS = '';
  }

  /**
   * Attaches the plugin by setting global CSS variables and returning CSS rules.
   * @param {HTMLElement} host - The shadow host element.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  attach(host) {
    if (host) {
      this.host = host;
    }
    host = this.host;
    host.style.setProperty('--fill-color', this.color);
    this.updateCSS();
    return [this.baseRule, this.additionalCSS];
  }

  /**
   * Detaches the plugin by removing global CSS variables.
   * @param {HTMLElement} host - The shadow host element.
   */
  detach(host) {
    host.style.removeProperty('--fill-color');
  }

  /**
   * Provides the updated CSS rules for fill color.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  getCSS() {
    return [this.baseRule, this.additionalCSS];
  }

  /**
   * Updates global configurations and returns the updated CSS.
   * @param {Object} options - Options to update.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  updateGlobal(options = {}) {
    if (options.color) this.color = options.color;
    return this.attach();
  }

  /**
   * Updates per-rectangle configurations by setting CSS variables on the rectangle.
   * @param {HTMLElement} rect - The rectangle element.
   * @param {Object} options - Options to update.
   */
  updateRect(rect, options = {}) {
    if (options.color) {
      rect.style.setProperty('--fill-color', options.color);
    }
  }

  /**
   * Cleans up per-rectangle configurations by removing CSS variables.
   * @param {HTMLElement} rect - The rectangle element.
   */
  onRectRemove(rect) {
    rect.style.removeProperty('--fill-color');
  }
}

class TransparencyPlugin extends PluginInterface {
  /**
   * Constructs a TransparencyPlugin instance with given options.
   * @param {Object} options - Configuration options.
   * @param {number|string} options.opacity - The opacity value (e.g., 0.5 or '50%').
   */
  constructor(options = {}) {
    super();
    this.opacity = options.opacity !== undefined ? options.opacity : 1; // Default opacity is 1 (fully opaque)
    this.host = null;
    this.updateCSS();
  }

  /**
   * Updates the CSS rules based on current configurations.
   */
  updateCSS() {
    this.baseRule = `
      opacity: var(--rect-opacity, ${this.opacity});
    `;

    // TransparencyPlugin doesn't have additional CSS rules
    this.additionalCSS = '';
  }

  /**
   * Attaches the plugin by setting global CSS variables and returning CSS rules.
   * @param {HTMLElement} host - The shadow host element.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  attach(host) {
    if (host) {
      this.host = host;
    }
    host = this.host;
    host.style.setProperty('--rect-opacity', this.opacity);
    this.updateCSS();
    return [this.baseRule, this.additionalCSS];
  }

  /**
   * Detaches the plugin by removing global CSS variables.
   * @param {HTMLElement} host - The shadow host element.
   */
  detach(host) {
    host.style.removeProperty('--rect-opacity');
  }

  /**
   * Provides the updated CSS rules for opacity.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  getCSS() {
    return [this.baseRule, this.additionalCSS];
  }

  /**
   * Updates global configurations and returns the updated CSS.
   * @param {Object} options - Options to update.
   * @returns [base, additional] - An array containing 'base' and 'additional' CSS rules.
   */
  updateGlobal(options = {}) {
    if (options.opacity !== undefined) this.opacity = options.opacity;
    return this.attach();
  }

  /**
   * Updates per-rectangle configurations by setting CSS variables on the rectangle.
   * @param {HTMLElement} rect - The rectangle element.
   * @param {Object} options - Options to update.
   */
  updateRect(rect, options = {}) {
    if (options.opacity !== undefined) {
      rect.style.setProperty('--rect-opacity', options.opacity);
    }
  }

  /**
   * Cleans up per-rectangle configurations by removing CSS variables.
   * @param {HTMLElement} rect - The rectangle element.
   */
  onRectRemove(rect) {
    rect.style.removeProperty('--rect-opacity');
  }
}



// Rectangle Wrapper
class RectWrapper {
  /**
   * Creates a RectWrapper instance.
   * @param {HTMLElement} rect - The rectangle element.
   * @param {RectRegionShapeFactory} factory - Reference to the factory.
   */
  constructor(rect, factory) {
    this.rect = rect;
    this.factory = factory;
    this.pluginOptions = new Map(); // To keep track of per-plugin options for this rectangle
  }

  /**
   * Removes the rectangle from the DOM and cleans up plugin-specific settings.
   */
  remove() {
    // Clean up plugin-specific settings
    this.factory.plugins.forEach((plugin, PluginClass) => {
      plugin.onRectRemove(this.rect);
    });

    // Remove the rectangle from the DOM
    this.rect.remove();
  }

  /**
   * Updates the rectangle's properties and applies plugin-specific updates.
   * @param {Object} positionalOptions - New positional and size properties.
   * @param {Array} pluginUpdates - Array of plugin updates in the format [[PluginClass, options], ...].
   */
  update(positionalOptions = {}, pluginUpdates = []) {
    const { x, y, width, height } = positionalOptions;

    // Update position
    if (x !== undefined) {
      this.rect.style.left = typeof x === 'number' ? `${x}px` : x;
    }

    if (y !== undefined) {
      this.rect.style.top = typeof y === 'number' ? `${y}px` : y;
    }

    // Update size
    if (width !== undefined) {
      this.rect.style.setProperty('--width', typeof width === 'number' ? `${width}px` : width);
    }

    if (height !== undefined) {
      this.rect.style.setProperty('--height', typeof height === 'number' ? `${height}px` : height);
    }

    // Apply plugin-specific updates
    pluginUpdates.forEach(([PluginClass, options]) => {
      const plugin = this.factory.plugins.get(PluginClass);
      if (plugin) {
        plugin.updateRect(this.rect, options);

        // Store the options for potential future use or cleanup
        if (!this.pluginOptions.has(PluginClass)) {
          this.pluginOptions.set(PluginClass, {});
        }
        const existingOptions = this.pluginOptions.get(PluginClass);
        this.pluginOptions.set(PluginClass, { ...existingOptions, ...options });
      } else {
        console.warn(`Plugin ${PluginClass.name} not found.`);
      }
    });

    // Note: No need to re-render global CSS for per-rectangle updates
  }
}

// Rectangle Factory
class RectRegionShapeFactory {
  /**
   * Constructor to initialize and mount on a container with optional plugins.
   * @param {HTMLElement} container - The DOM element to mount the rectangles.
   * @param {Array<PluginInterface>} plugins - Array of plugin instances.
   */
  constructor(container, plugins = []) {
    if (!(container instanceof HTMLElement)) {
      throw new Error('Container must be a valid DOM element.');
    }

    this.container = container;
    this.plugins = new Map();

    // Create a shadow root on the container
    this.shadow = container.attachShadow({ mode: 'open' });

    // Initialize a style element inside the shadow DOM
    this.styleElement = document.createElement('style');
    this.shadow.appendChild(this.styleElement);

    // Initialize a container inside the shadow DOM to hold rectangles
    this.rectContainer = document.createElement('div');
    this.rectContainer.style.position = 'relative'; // Ensures absolute children are positioned correctly
    this.shadow.appendChild(this.rectContainer);

    this.baseRules = `
        position: absolute;
        width: var(--width, 100px);
        height: var(--height, 100px);
        box-sizing: border-box;
    `.trim();

    // Debounce renderCSS calls using a microtask queue
    this.debounceTask = null;

    // Register and initialize plugins
    plugins.forEach(plugin => {
      this.registerPlugin(plugin);
    });

    // Initially render all CSS
    this.renderCSS();
  }

  /**
   * Schedules renderCSS to run as a microtask.
   */
  scheduleRenderCSS() {
    if (!this.debounceTask) {
      this.debounceTask = Promise.resolve().then(() => {
        this.renderCSS();
        this.debounceTask = null;
      });
    }
  }

  /**
   * Registers a plugin by attaching it and storing it internally.
   * @param {PluginInterface} plugin - The plugin instance.
   */
  registerPlugin(plugin) {
    if (this.plugins.has(plugin.constructor)) {
      console.warn(`Plugin ${plugin.constructor.name} is already registered.`);
      return;
    }
    this.plugins.set(plugin.constructor, plugin);
    plugin.attach(this.shadow.host);
    this.scheduleRenderCSS();
  }

  /**
   * Unregisters a plugin by detaching it and removing its CSS.
   * @param {Function} PluginClass - The plugin's class.
   */
  unregisterPlugin(PluginClass) {
    const plugin = this.plugins.get(PluginClass);
    if (plugin) {
      plugin.detach(this.shadow.host);
      this.plugins.delete(PluginClass);
      this.scheduleRenderCSS();
    } else {
      console.warn(`Plugin ${PluginClass.name} not found.`);
    }
  }

  /**
   * Updates a plugin's global configurations.
   * @param {Function} PluginClass - The plugin's class.
   * @param {Object} options - Options to update.
   */
  updateGlobalPlugin(PluginClass, options = {}) {
    const plugin = this.plugins.get(PluginClass);
    if (plugin) {
      // Update the plugin's global configurations
      plugin.updateGlobal(options);
      this.scheduleRenderCSS();
    } else {
      console.warn(`Plugin ${PluginClass.name} not found.`);
    }
  }

  /**
   * Aggregates CSS from all plugins and the base styles, then injects into the style element.
   */
  renderCSS() {
    // Initialize baseRules with the standard .rect-region styles
    const baseRulesArray = [this.baseRules];
    
    const additionalRulesArray = []; // Initialize an empty array for additional CSS rules

    // Aggregate CSS from all registered plugins
    this.plugins.forEach(plugin => {
      const css = plugin.getCSS();
      let [base, additional] = css;
      base = base.trim();
      additional = additional.trim();
      if (base) {
        baseRulesArray.push(base);
      }
      if (additional) {
        additionalRulesArray.push(additional);
      }
    });

    // Combine base and additional CSS rules by joining the arrays with newlines
    const aggregatedCSS = `
      .rect-region {
        ${baseRulesArray.join('\n')}
      }
      ${additionalRulesArray.join('\n')}
    `;

    // Inject the aggregated CSS into the shadow DOM's style element
    this.styleElement.textContent = aggregatedCSS;
  }

  /**
   * Creates and appends a rectangle wrapped with plugins.
   * @param {number} x - The left position in pixels relative to the container.
   * @param {number} y - The top position in pixels relative to the container.
   * @param {number} width - Width of the rectangle in pixels.
   * @param {number} height - Height of the rectangle in pixels.
   * @returns {RectWrapper} - The wrapper object managing the rectangle.
   */
  createRect(x, y, width, height) {
    // Create the rectangle div
    const rect = document.createElement('div');
    rect.classList.add('rect-region');

    // Set position relative to the shadow container
    rect.style.left = `${x}px`;
    rect.style.top = `${y}px`;

    // Set width and height via CSS variables
    rect.style.setProperty('--width', `${width}px`);
    rect.style.setProperty('--height', `${height}px`);

    // Append the rectangle to the shadow DOM's container
    this.rectContainer.appendChild(rect);

    // Instantiate the wrapper with reference to this factory and the rectangle
    const rectWrapper = new RectWrapper(rect, this);
    return rectWrapper;
  }
}

// Usage Example

// Instantiate plugins
const plugins = [
  new BorderPlugin({ size: '3px', color: '#2c3e50' }),
  new VertexPlugin({ size: '20px', color: '#e74c3c' }),
  new FillPlugin({ color: '#2ecc71' }), // New FillPlugin
  new TransparencyPlugin({ opacity: 0.8 }) // New TransparencyPlugin
];

// Get the container element
const container = document.getElementById('rectangle-container');

// Instantiate the factory with plugins
const factory = new RectRegionShapeFactory(container, plugins);

// Create multiple rectangles with different properties
const rect1 = factory.createRect(50, 50, 200, 100);
const rect2 = factory.createRect(300, 150, 250, 150);
const rect3 = factory.createRect(600, 80, 180, 120); // Uses plugin defaults

// Example: Update rect1 after 2 seconds
    // Example: Update rect1 after 2 seconds
setTimeout(() => {
  rect1.update(
    { x: 60, y: 60, width: 220, height: 120 },
    [
      [BorderPlugin, { size: '4px', color: '#8e44ad' }],
      [VertexPlugin, { size: '25px', color: '#2980b9' }],
      [FillPlugin, { color: '#16a085' }],
      [TransparencyPlugin, { opacity: 0.85 }]
    ]
  );
}, 2000);

// Example: Remove rect2 after 4 seconds
setTimeout(() => {
  rect2.remove();
}, 4000);

// Example: Update global plugin settings after 6 seconds
setTimeout(() => {
      factory.updateGlobalPlugin(BorderPlugin, { size: '5px', color: 'red' });
      factory.updateGlobalPlugin(VertexPlugin, { size: '30px', color: '#f1c40f' });
      factory.updateGlobalPlugin(FillPlugin, { color: '#34495e' });
      factory.updateGlobalPlugin(TransparencyPlugin, { opacity: 0.5 });
  // Create a new rectangle to see updated global settings
  const rect4 = factory.createRect(100, 300, 150, 150);
  const rect5 = factory.createRect(400, 350, 200, 100);
  
      rect3.update(
      { x: 600, y: 80, width: 180, height: 120 },
      [
        [FillPlugin, { color: '#8e44ad' }],
        [TransparencyPlugin, { opacity: 0.1 }]
      ]
    );
  
}, 6000);

// Example: Unregister a plugin after 8 seconds (e.g., remove VertexPlugin)

```


extend the code with drag and drop with these plugins:
"""
class DndPlugin extends PluginInterface {
  onRectCreate(rect) {
    // set up listeners
  }

  onRectRemove(rect) {
    // remove listeners
  }
}

// resize when dragging edges or vertices
class DndMovePlugin extends PluginInterface {
  constructor(...) {
    // states, since we dnd on 1 rect at a time we dont need separate instances
  }

  isApplicable(rect, pos) { // maybe use a better method name
     // determine if start drag position is valid for this plugin
    return bool;
  }

  onDragStart(rect, pos) {
    
  }
  onDragMove(rect, pos) {

  }
  onDragEnd(rect, pos) {

  }
}

// resize when dragging edges or vertices
class DndResizePlugin extends PluginInterface {
  constructor(...) {
     // states, since we dnd on 1 rect at a time we dont need separate instances
  }

  isApplicable(rect, pos) {  // maybe use a better method name
     // determine if start drag position is valid for this plugin
    return bool;
  }

  onDragStart(rect, pos) {
    
  }
  onDragMove(rect, pos) {

  }
  onDragEnd(rect, pos) {

  }
}
"""