Skip to content

Repository files navigation

Developing Apps for Cytoscape Web

⚠️ Developer Preview

The SDK and scaffolder are published at 0.x. An app runs inside Cytoscape Web with the host's full privileges — no sandbox, no signature verification, so nothing here is ready to carry production work. That is a property of the platform today, not of this tooling.

An app runs inside Cytoscape Web with the host's full privileges — same origin, DOM, storage and network identity. There is no sandbox and no signature verification, and an app can read the user's credentials. Install only apps you trust, and understand that asking someone to install yours asks the same of them.

The API may change before 1.0. What is missing before it can be called ready is host-side: app isolation, a capability API, and artifact integrity.

Reference implementations and documentation for building apps for Cytoscape Web.

Introduction

This repository is for third-party developers who want to build apps for Cytoscape Web.

You do not need to change the host source code. Your app is loaded by the host through Module Federation (Vite). Apps can add:

  • panel components in the right-side App Panel
  • menu items in the Apps dropdown
  • context menu actions for right-click workflows

Quick Start

npm create cytoscape-app my-app
cd my-app
npm run dev

The dev server prints the link that installs your app into a running local host:

  Cytoscape Web app myApp — http://localhost:6001

  Install it into http://localhost:5500/:
  http://localhost:5500/?installApp=http://localhost:6001/cyweb-app.json

Start a host (npm run dev in a cytoscape-web checkout), open that link, confirm the install, and enable the app under Apps → App Settings.

Or skip the host checkout entirely and develop against the shared staging host, with your app still on localhost:

CYWEB_DEV_HOST=https://dev1.ndexbio.org/cytoscape npm run dev

The printed link then points at dev1. Your browser asks once for permission to reach your own machine — click Allow; see getting-started §5d for what that prompt says and what to do if you have already dismissed it.

Needs @cytoscape-web/app-runtime 0.2.0 or newer. 0.1.0 ignores the variable silently — the banner still names localhost:5500 and the app still loads the local host, which is the only tell.

Nothing in the host repository is edited. Your dev server serves a one-entry app manifest at /cyweb-app.json, generated from your package.json on every request, and the host has accepted ?installApp= all along.

If it behaves like an older version, it probably is one. npm create can run a cached copy of the scaffolder without checking the registry, so anyone who ran it before keeps that one. npm create cytoscape-app@latest my-app names the tag explicitly — how to tell, and how to clear it.

Your app does not hot-reload inside the host. Vite's HMR does not cross the federation boundary — that is a separate feature (dev.remoteHmr), off by default. Your dev server rebuilds the changed module immediately; reload the host page to pick it up.

Working on this repository instead

If you want to run the four example apps rather than build your own:

git clone https://github.com/cytoscape/cytoscape-web.git
git clone https://github.com/cytoscape/cytoscape-web-app-examples.git

cd cytoscape-web-app-examples && npm install && npm run dev   # all four apps
cd ../cytoscape-web          && npm install && npm run dev    # the host, :5500

Then open http://localhost:5500, Apps → App Settings, and enable one. The dev server reads src/assets/apps.local.json, which already lists them.

Publishing to the public Cytoscape Web site

Still manual, and deliberately so. The production instance loads apps from a curated allowlist (apps.json) maintained by the core team; a self-service App Store is designed but not built, and its launch is gated on the same host-side isolation work as the banner above. To publish, please contact the Cytoscape team.


Build Your First App

npm create cytoscape-app my-app
cd my-app

That is the whole setup — it installs dependencies and leaves a project that builds, verifies and tests as it stands. Add --yes with --id, --port and --template to skip the prompts entirely; every prompt has a flag, so an agent can drive it without a terminal.

Prefer to start from a worked example? cp -r project-template my-app still works and gives you the same shape by hand.

Then two things:

1. Your identity, in package.json. Written once, and read from there by the build, by the app config, and by the install manifest:

{
  "name": "@you/my-app",
  "version": "0.1.0",
  "description": "What your app does — shown in App Settings",
  "cyweb": { "id": "myApp", "displayName": "My App", "port": 6001 }
}

cyweb.id is the Module Federation container name, the CyApp.id and the id the host registers, all at the same time.

2. Your app, in src/. Replace the panel and menu components, and adjust resources in src/MyApp.tsx. vite.config.ts is three lines and needs no edits.

Then npm run dev. It prints the link that installs your app into a running local host:

  Cytoscape Web app myApp — http://localhost:6001

  Install it into http://localhost:5500/:
  http://localhost:5500/?installApp=http://localhost:6001/cyweb-app.json

Nothing in the host repository is edited. The dev server serves a one-entry manifest at /cyweb-app.json, generated from your package.json on each request, and the host has accepted ?installApp= all along.

See project-template/README.md for details.


App Entry Point

Every app exports one CyAppWithLifecycle object:

import { lazy } from 'react'
import { CyAppWithLifecycle } from 'cyweb/ApiTypes'
// Identity, from the `cyweb` block and the standard fields in package.json.
// Do NOT `import packageJson from '../package.json'` — that pulls the whole
// file, devDependencies included, into your browser bundle to read one string.
// `cyweb-app verify` fails a build that does.
import { description, displayName, id, version } from 'virtual:cyweb-app-meta'

export const MyApp: CyAppWithLifecycle = {
  // Written once, in package.json:
  //   "cyweb": { "id": "myApp", "displayName": "My App", "port": 6001 }
  // `id` is the Module Federation container name, this CyApp's id and the id
  // the host registers, all at the same time.
  id,
  name: displayName,
  description,
  version,
  apiVersion: '1.0',

  // Declarative resource registration — panels and menu items
  resources: [
    {
      slot: 'right-panel',
      id: 'MyPanel',
      title: 'My Panel',
      component: lazy(() => import('./components/MyPanel')),
    },
    // An 'apps-menu' entry is plain data: the host renders the row and
    // closes the dropdown itself. Put any UI behind it in a dialog opened
    // from onClick via apis.dialog.open({ title, render }).
    {
      slot: 'apps-menu',
      id: 'MyMenuItem',
      label: 'My Action',
      onClick: (apis) => {
        apis.network.createNetworkFromEdgeList({ name: 'Example', edgeList: [['A', 'B']] })
      },
    },
  ],

  // Context menus and event listeners — registered in mount()
  mount(context) {
    context.apis.contextMenu.addContextMenuItem({
      label: 'My App: Log Node Info',
      targetTypes: ['node'],
      handler: (ctx) => {
        const result = context.apis.element.getNode(ctx.networkId, ctx.id!)
        if (result.success) console.info('Node:', result.data)
      },
    })
  },
  unmount() {
    /* clean up event listeners only — context menus are auto-cleaned */
  },
}

Documentation Map

Developer Guides

Guide Topics
Getting Started Scaffold, configure, register, run
Architecture Overview Module Federation, type system, API layers
Registration Patterns Panels, menus, context menus, upsert, batch
Lifecycle & Cleanup mount/unmount, auto-cleanup, re-enable
Troubleshooting Build errors, runtime errors, FAQ

API Reference

Resource Description
App API Reference Complete reference for all domain APIs, ResourceApi, Event Bus, error codes, and lifecycle
@cytoscape-web/api-types (README) TypeScript types package — install for IDE support
CHANGELOG Version history for the types package

Specifications (Advanced)

Spec Scope
App API Specification Full 2000-line spec for all 10 domain APIs
Resource Registration Specification Slot model, lifecycle, cleanup, error boundaries
Registration Minimal App Example End-to-end code walkthrough of all registration paths

Available APIs

All API methods return ApiResult<T>. Always check result.success before reading result.data.

API Import Purpose
WorkspaceApi cyweb/WorkspaceApi Current network ID, workspace info, switch network
ElementApi cyweb/ElementApi Create/delete nodes and edges, graph traversal queries
NetworkApi cyweb/NetworkApi Create/delete networks, import CX2
SelectionApi cyweb/SelectionApi Read and mutate the current selection
ViewportApi cyweb/ViewportApi Pan, zoom, fit, read/write node positions
TableApi cyweb/TableApi Read and write node/edge attribute tables
VisualStyleApi cyweb/VisualStyleApi Set defaults, bypasses, and mappings
LayoutApi cyweb/LayoutApi Run layout algorithms
ExportApi cyweb/ExportApi Export network as CX2
EventBus cyweb/EventBus Subscribe to host events (useCyWebEvent)
AppIdContext cyweb/AppIdContext Per-app context (useAppContext) for resource and context menu APIs
ApiTypes cyweb/ApiTypes TypeScript types for all of the above

Available Events

Event Fires when
network:created A new network is added to the workspace
network:deleted A network is removed
network:switched The user navigates to a different network
selection:changed Node or edge selection changes
layout:started A layout algorithm begins
layout:completed A layout algorithm finishes
style:changed A visual style property changes
data:changed Node or edge attribute data changes

Non-React Access

Outside React components, the same APIs are available via window.CyWebApi:

window.addEventListener('cywebapi:ready', () => {
  const api = window.CyWebApi
  const result = api.workspace.getCurrentNetworkId()
  // ...
})

Note: window.CyWebApi does not include resource or per-app contextMenu. Those are only available inside mount() via context.apis or via useAppContext().


Example Apps

Example Best for Details
project-template/ Your first app — panel, menu action, and context menu README
hello-world/ Full API coverage — 13 examples covering all APIs README
network-statistics/ Non-React — graph traversal, event-driven logging README
network-workflows/ CX2 import, Jupyter integration, menu workflows README

Recommended reading order: project-template → hello-world → network-statistics → network-workflows


Type Setup

Install the types package for IDE support:

npm install --save-dev @cytoscape-web/api-types @cytoscape-web/app-runtime

Reference both packages' bundled declarations from your tsconfig.json so TypeScript resolves the cyweb/* ambient modules and virtual:cyweb-app-meta:

{
  "include": ["src/**/*"],
  "compilerOptions": {
    "moduleResolution": "bundler",
    "types": ["@cytoscape-web/api-types", "@cytoscape-web/app-runtime/meta"]
  }
}

Listing them in types is what pulls in the ambient cyweb/* and virtual:cyweb-app-meta declarations — the same mechanism vite/client uses. Do not set typeRoots: the example apps used to point it at ./node_modules/@types, a directory that does not exist in a workspace, and setting it suppresses the default lookup that actually finds the types. See any of the example apps' tsconfig.json for a working reference.


Development Commands

npm run dev                     # run all workspaces concurrently
npm run dev:hello-world         # run one app
npm run dev:network-statistics
npm run dev:network-workflows
npm run dev:project-template
npm run build                   # build all workspaces
npm run deploy                  # build and copy each workspace's dist/ into docs/

Verification

npm run verify:federation       # the built dist/ has the right federation shape
npm run preflight:host -- <hostUrl>              # the host publishes a usable descriptor
npm run preflight:apps -- <hostUrl> <appsBase>   # the PUBLISHED apps load in that host

The three cover different layers, and the gap between the first two is why the third exists. verify:federation reads dist/, preflight:host reads the host — so a fault in the serving layer between them is invisible to both. That is not hypothetical: GitHub Pages ran this repo's docs/ through Jekyll, which drops _-prefixed paths, and silently 404'd the _virtual_mf-* chunk every app imports first while both other checks stayed green.

preflight:apps loads each published app through a real dynamic import() inside a real host page, so transitive chunk fetches, CORS and MIME are the real ones. -- --selftest proves it can still fail.


Deprecated APIs

Older examples used direct store imports. They still work, but new apps should use the App API hooks instead — they return ApiResult<T> and provide a stable, documented contract. See Architecture Overview → Host Exposes Reference for the full list of legacy cyweb/*Store exposes.

Deprecated pattern Recommended replacement
useNetworkStore useNetworkApi
useTableStore useTableApi
useWorkspaceStore useWorkspaceApi

About

Example Apps for Cytoscape Web.

Resources

Stars

6 stars

Watchers

10 watching

Forks

Releases

Packages

Used by

Contributors

Languages