or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-management.mdelement-collections.mdevent-system.mdextensions.mdgraph-algorithms.mdindex.mdlayout-system.mdstyling-system.md
tile.json

extensions.mddocs/

Extensions

Plugin architecture for extending cytoscape functionality with custom layouts, renderers, utility functions, and third-party integrations. Supports both core extensions and collection extensions.

Capabilities

Extension Registration

Register extensions to add new functionality to cytoscape instances and collections.

/**
 * Register extension using cytoscape.use()
 * @param extension - Extension object or function
 */
cytoscape.use(extension: any): void;

/**
 * Register extension by type and name
 * @param type - Extension type ("core", "collection", "layout", "renderer")
 * @param name - Extension name
 * @param extension - Extension implementation
 */
cytoscape(type: string, name: string, extension: any): void;

/**
 * Get registered extension
 * @param type - Extension type
 * @param name - Extension name
 * @returns Extension instance or undefined
 */
cytoscape(type: string, name: string): any;

Usage Examples:

// Register extension using .use()
import myExtension from 'my-cytoscape-extension';
cytoscape.use(myExtension);

// Register extension by type
cytoscape('layout', 'myLayout', MyLayoutExtension);
cytoscape('core', 'myUtility', MyCoreExtension);

// Get registered extension
const layoutExt = cytoscape('layout', 'myLayout');

Core Extensions

Extend the Core prototype with new methods and properties.

interface CoreExtension {
  /**
   * Extension function that receives core instance
   * @param core - Cytoscape core instance
   */
  (core: Core): void;
}

/**
 * Core extension registration
 */
cytoscape('core', 'extensionName', function(core: Core) {
  // Add methods to core prototype
  core.myCustomMethod = function() {
    // Custom functionality
    return this; // Return core for chaining
  };
  
  // Add properties
  core.myProperty = 'value';
});

// Alternative syntax using extension object
const coreExtension = {
  myMethod: function() {
    // 'this' refers to the core instance
    return this.nodes().length;
  },
  
  myUtility: function(param) {
    // Custom utility function
    console.log('Core utility called with:', param);
    return this;
  }
};

cytoscape('core', 'myExtension', coreExtension);

Usage Examples:

// Define core extension
cytoscape('core', 'analytics', function() {
  return {
    getStats: function() {
      return {
        nodeCount: this.nodes().length,
        edgeCount: this.edges().length,
        selectedCount: this.$(':selected').length
      };
    },
    
    highlightPath: function(source, target) {
      const dijkstra = this.elements().dijkstra({
        root: source,
        directed: false
      });
      
      if (dijkstra.hasPathTo(target)) {
        const path = dijkstra.pathTo(target);
        path.addClass('highlighted-path');
        return path;
      }
      
      return this.collection();
    }
  };
});

// Use core extension
const cy = cytoscape({ /* options */ });
const stats = cy.analytics().getStats();
console.log('Graph stats:', stats);

const path = cy.analytics().highlightPath('#start', '#end');

Collection Extensions

Extend the Collection prototype with new methods for element manipulation.

interface CollectionExtension {
  /**
   * Extension function that receives collection instance
   * @param collection - Element collection
   */
  (collection: Collection): void;
}

/**
 * Collection extension registration
 */
cytoscape('collection', 'extensionName', function(collection: Collection) {
  // Add methods to collection prototype
  collection.myCustomMethod = function() {
    // Custom functionality on elements
    return this; // Return collection for chaining
  };
});

// Alternative object syntax
const collectionExtension = {
  centerOnViewport: function() {
    const cy = this.cy();
    const bb = this.boundingBox();
    
    cy.pan({
      x: (cy.width() / 2) - (bb.x1 + bb.w / 2),
      y: (cy.height() / 2) - (bb.y1 + bb.h / 2)
    });
    
    return this;
  },
  
  fadeOut: function(duration = 1000) {
    return this.animate({
      'opacity': 0
    }, duration);
  },
  
  fadeIn: function(duration = 1000) {
    return this.animate({
      'opacity': 1
    }, duration);
  }
};

cytoscape('collection', 'animations', collectionExtension);

Usage Examples:

// Define collection extension
cytoscape('collection', 'utilities', {
  getNeighborhoodData: function() {
    const data = [];
    this.forEach(node => {
      if (node.isNode()) {
        data.push({
          id: node.id(),
          degree: node.degree(),
          neighbors: node.neighbors().length,
          data: node.data()
        });
      }
    });
    return data;
  },
  
  applyTemplate: function(template) {
    this.style(template.style);
    if (template.classes) {
      this.addClass(template.classes);
    }
    return this;
  },
  
  savePositions: function() {
    const positions = {};
    this.nodes().forEach(node => {
      positions[node.id()] = node.position();
    });
    return positions;
  },
  
  restorePositions: function(positions) {
    this.nodes().forEach(node => {
      const pos = positions[node.id()];
      if (pos) {
        node.position(pos);
      }
    });
    return this;
  }
});

// Use collection extension
const selectedNodes = cy.nodes(':selected');
const neighborhoodData = selectedNodes.utilities().getNeighborhoodData();

const positions = cy.nodes().utilities().savePositions();
// ... later ...
cy.nodes().utilities().restorePositions(positions);

Layout Extensions

Create custom layout algorithms for automatic node positioning.

interface LayoutExtension {
  /**
   * Layout name
   */
  name: string;
  
  /**
   * Layout run function
   * @param options - Layout options
   * @returns Layout instance
   */
  run(options?: any): Layout;
  
  /**
   * Layout stop function
   * @returns Layout instance
   */
  stop?(): Layout;
}

/**
 * Layout extension registration
 */
cytoscape('layout', 'layoutName', function(options) {
  const layout = this;
  
  // Layout implementation
  layout.run = function() {
    const nodes = layout.options.eles.nodes();
    const edges = layout.options.eles.edges();
    
    // Custom positioning logic
    nodes.forEach((node, i) => {
      node.position({
        x: i * 100,
        y: Math.sin(i * 0.5) * 100
      });
    });
    
    // Emit layout events
    layout.emit('layoutready');
    layout.emit('layoutstop');
    
    return layout;
  };
  
  layout.stop = function() {
    // Stop layout if running
    return layout;
  };
  
  return layout;
});

Usage Examples:

// Define custom spiral layout
cytoscape('layout', 'spiral', function(options) {
  const defaults = {
    radius: 100,
    turns: 3,
    angleStep: 0.1
  };
  
  const layout = this;
  const opts = Object.assign({}, defaults, options);
  
  layout.run = function() {
    const nodes = layout.options.eles.nodes();
    let angle = 0;
    
    nodes.forEach((node, i) => {
      const r = (opts.radius * angle) / (2 * Math.PI * opts.turns);
      
      node.position({
        x: r * Math.cos(angle),
        y: r * Math.sin(angle)
      });
      
      angle += opts.angleStep;
    });
    
    if (opts.fit) {
      layout.cy.fit(nodes, opts.padding);
    }
    
    layout.emit('layoutready');
    layout.emit('layoutstop');
    
    return layout;
  };
  
  return layout;
});

// Use custom layout
cy.layout({
  name: 'spiral',
  radius: 150,
  turns: 4,
  angleStep: 0.2,
  fit: true,
  padding: 50
}).run();

Renderer Extensions

Create custom rendering backends for specialized visualization needs.

interface RendererExtension {
  /**
   * Renderer name
   */
  name: string;
  
  /**
   * Initialize renderer
   * @param cy - Cytoscape core instance
   * @param options - Renderer options
   */
  initialize(cy: Core, options: any): void;
  
  /**
   * Destroy renderer and clean up
   */
  destroy?(): void;
}

/**
 * Renderer extension registration
 */
cytoscape('renderer', 'rendererName', RendererImplementation);

Usage Examples:

// Register custom canvas renderer
cytoscape('renderer', 'mycanvas', MyCanvasRenderer);

// Use custom renderer
const cy = cytoscape({
  container: document.getElementById('cy'),
  renderer: {
    name: 'mycanvas',
    options: {
      // Renderer-specific options
    }
  },
  elements: [/* ... */]
});

Extension Development Patterns

Best practices and common patterns for extension development.

/**
 * Extension factory pattern
 */
function createExtension(name, implementation) {
  return function(cytoscape) {
    if (!cytoscape) {
      return;
    }
    
    cytoscape('core', name, implementation.core || {});
    cytoscape('collection', name, implementation.collection || {});
    
    if (implementation.layout) {
      cytoscape('layout', name, implementation.layout);
    }
  };
}

/**
 * Extension with options pattern
 */
function createConfigurableExtension(defaultOptions) {
  return function(cytoscape) {
    cytoscape('core', 'myExt', function(options) {
      const opts = Object.assign({}, defaultOptions, options);
      
      return {
        doSomething: function() {
          // Use opts for configuration
          return this;
        }
      };
    });
  };
}

/**
 * Extension with initialization pattern
 */
function createInitializableExtension() {
  let initialized = false;
  
  return function(cytoscape) {
    cytoscape('core', 'myExt', function() {
      if (!initialized) {
        // One-time initialization
        this.on('ready', function() {
          console.log('Extension initialized');
        });
        initialized = true;
      }
      
      return {
        // Extension methods
      };
    });
  };
}

Third-Party Extension Integration

Common patterns for using popular cytoscape extensions.

/**
 * Layout extensions
 */
import cola from 'cytoscape-cola';
import dagre from 'cytoscape-dagre';
import klay from 'cytoscape-klay';
import euler from 'cytoscape-euler';

cytoscape.use(cola);
cytoscape.use(dagre);
cytoscape.use(klay);
cytoscape.use(euler);

// Use third-party layouts
cy.layout({
  name: 'cola',
  nodeSpacing: 5,
  edgeLengthRate: 45,
  animate: true
}).run();

cy.layout({
  name: 'dagre',
  nodeSep: 10,
  rankSep: 10,
  rankDir: 'TB'
}).run();

/**
 * Interaction extensions
 */
import edgehandles from 'cytoscape-edgehandles';
import nodeResize from 'cytoscape-node-resize';
import contextMenus from 'cytoscape-context-menus';

cytoscape.use(edgehandles);
cytoscape.use(nodeResize);
cytoscape.use(contextMenus);

// Configure extensions
cy.edgehandles({
  canConnect: function(sourceNode, targetNode) {
    return !sourceNode.same(targetNode);
  },
  edgeParams: function(sourceNode, targetNode) {
    return {};
  }
});

cy.contextMenus({
  menuItems: [
    {
      id: 'remove',
      content: 'Remove',
      tooltipText: 'Remove element',
      selector: 'node, edge',
      onClickFunction: function(event) {
        event.target.remove();
      }
    }
  ]
});

/**
 * Export extensions
 */
import panzoom from 'cytoscape-panzoom';
import navigator from 'cytoscape-navigator';

cytoscape.use(panzoom);
cytoscape.use(navigator);

// Add UI controls
cy.panzoom({
  zoomFactor: 0.05,
  zoomDelay: 45,
  minZoom: 0.1,
  maxZoom: 10
});

cy.navigator({
  container: document.getElementById('nav'),
  viewLiveFramerate: 0,
  thumbnailEventFramerate: 30
});

Extension Testing and Debugging

Tools and techniques for extension development.

/**
 * Extension testing pattern
 */
function testExtension() {
  // Create test instance
  const cy = cytoscape({
    elements: [
      { data: { id: 'a' } },
      { data: { id: 'b' } },
      { data: { id: 'ab', source: 'a', target: 'b' } }
    ]
  });
  
  // Test extension methods
  console.assert(typeof cy.myExtension === 'function', 'Extension not registered');
  console.assert(cy.nodes().myMethod, 'Collection method not available');
  
  // Test functionality
  const result = cy.myExtension().doSomething();
  console.assert(result === cy, 'Method should return core for chaining');
}

/**
 * Extension debugging utilities
 */
const debugExtension = {
  logMethodCalls: function(target, methods) {
    methods.forEach(method => {
      const original = target[method];
      target[method] = function(...args) {
        console.log(`Called ${method} with:`, args);
        return original.apply(this, args);
      };
    });
  },
  
  validateExtension: function(extension, requiredMethods) {
    requiredMethods.forEach(method => {
      if (typeof extension[method] !== 'function') {
        console.warn(`Extension missing required method: ${method}`);
      }
    });
  }
};

Extension Distribution

Package and distribute extensions for the community.

/**
 * Extension module pattern
 */
(function(factory) {
  // UMD pattern for broad compatibility
  if (typeof module !== 'undefined' && module.exports) {
    // CommonJS
    module.exports = factory(require('cytoscape'));
  } else if (typeof define === 'function' && define.amd) {
    // AMD
    define(['cytoscape'], factory);
  } else {
    // Global
    if (typeof cytoscape !== 'undefined') {
      factory(cytoscape);
    }
  }
})(function(cytoscape) {
  'use strict';
  
  if (!cytoscape) {
    return;
  }
  
  // Extension implementation
  const implementation = {
    // Extension code here
  };
  
  // Register extension
  cytoscape('core', 'myExtension', implementation);
  
  // Return for chaining
  return cytoscape;
});

/**
 * Package.json for extension
 */
{
  "name": "cytoscape-my-extension",
  "version": "1.0.0",
  "description": "My custom cytoscape extension",
  "main": "index.js",
  "peerDependencies": {
    "cytoscape": "^3.0.0"
  },
  "keywords": [
    "cytoscape",
    "extension",
    "graph",
    "visualization"
  ]
}