Reference (hooks, commands, schemas, endpoints)#

This page is the “source of truth” reference for the notebook hooks system.

Primary sources in this repo:

  • Python: cellucid-python/src/cellucid/jupyter.py

  • Python: cellucid-python/src/cellucid/_server_base.py

  • Web app: cellucid/assets/js/data/jupyter-source.js

  • Web app: cellucid/assets/js/app/main.js

If you are new, start with:


Public Python API (what you can call)#

Creating a viewer#

from cellucid import show, show_anndata
  • show("./export_dir", height=600) -> CellucidViewer

  • show_anndata(...) -> AnnDataViewer, with this closed call surface:

    show_anndata(
        data,
        height=600,
        *,
        latent_key=None,
        gene_id_column=None,
        normalize_embeddings=True,
        centroid_outlier_quantile=0.95,
        centroid_min_points=10,
        dataset_name=<required string>,
        dataset_id=<required string>,
        vector_field_default=None,
        client_server_url=None,
        web_source_url="https://www.cellucid.com",
        web_cache_dir=None,
    )
    

    vector_field_default is required when direct AnnData declares more than one vector field. The convenience function does not accept port; construct AnnDataViewer for a fixed port.

Basic properties#

  • viewer.server_url → the underlying data server URL (usually http://127.0.0.1:<port>)

  • viewer.viewer_url → the iframe URL (includes jupyter=true, viewerId, viewerToken, and anndata=true for AnnDataViewer)

Display and lifecycle#

  • viewer.display() → (re)display iframe in a notebook output cell

  • viewer.stop() → freeze the displayed view, stop the server, unregister hooks, and report any freeze or shutdown failure

  • cellucid.jupyter.cleanup_all() → stop all active viewers (also registered via atexit)

Commands (Python → viewer)#

  • viewer.send_message(message: dict) (low-level entry point for the closed command schemas below)

  • viewer.highlight_cells(cell_indices: list[int], color: str = "#ff0000")

  • viewer.clear_highlights()

  • viewer.set_color_by(field: str)

  • viewer.set_visibility(cell_indices: list[int] | None = None, visible: bool = True)

  • viewer.reset_view()

Hooks (viewer → Python)#

Decorators:

  • @viewer.on_ready

  • @viewer.on_selection

  • @viewer.on_hover

  • @viewer.on_click

  • @viewer.on_message (catches all seven current event types)

Programmatic registration:

  • viewer.register_hook(event: str, callback) -> callback

  • viewer.unregister_hook(event: str, callback) -> bool

  • viewer.clear_hooks(event: str | None = None)

Synchronous “pull” API:

  • viewer.state (latest event snapshot)

  • viewer.wait_for_event(event: str, timeout: float | None = 30.0, predicate=None) -> dict

  • viewer.wait_for_ready(timeout: float | None = 30.0) -> dict

Session bundles (durable state)#

  • bundle = viewer.get_session_bundle(timeout: float | None = 60.0)

  • bundle.save("path/to/file.cellucid-session")

  • adata2 = viewer.apply_session_to_anndata(adata, inplace=False)

  • adata2 = bundle.apply_to_anndata(adata, expected_dataset_id="my-study-v1", inplace=False)

  • Function-level: cellucid.apply_cellucid_session_to_anndata(...)

Connectivity / cache utilities#

  • viewer.debug_connection(timeout=5.0) → structured connectivity report, including dataset_identity_probes, keyed by every exact server-declared dataset id/path

  • viewer.ensure_web_ui_cached(force=True, show_progress=True) → establish the complete source UI generation

  • viewer.ensure_web_ui_cached(force=False, show_progress=True) → verify the selected existing generation without network access or mutation

  • viewer.clear_web_cache() → clear cached viewer UI assets


Event schemas (viewer → Python)#

Browser events are delivered via POST /_cellucid/events and routed by the authenticated viewerId/viewerToken pair. session_bundle is generated by the authenticated Python upload endpoint.

Python receives payloads without type, viewerId, or viewerToken. Every listed field is required and undeclared fields are rejected.

ready#

{"n_cells": int, "dimensions": int}

selection#

{"cells": list[int], "source": str}

hover#

{"cell": int | None, "position": dict | None}

click#

{"cell": int, "button": int, "shift": bool, "ctrl": bool}

pong#

{"requestId": str, "t": int}

debug_snapshot#

{
  "requestId": str,
  "ts": str,
  "locationHref": str,
  "origin": str,
  "serverUrl": str,
  "connected": bool,
  "parentOrigin": str,
  "userAgent": str | None,
}

session_bundle#

{
  "requestId": str,
  "status": "ok",
  "bytes": int,
  "path": str,
}

@viewer.on_message envelope#

For each current event type X, @viewer.on_message receives:

{"event": "X", **payload}

Command schemas (Python → viewer)#

All commands are sent via postMessage into the iframe.

You typically send only the message-specific fields; the embedding layer injects:

  • viewerId

  • viewerToken

highlight#

{"type": "highlight", "cells": [0, 10, 42], "color": "#ff00ff"}

clearHighlights#

{"type": "clearHighlights"}

setColorBy#

{"type": "setColorBy", "field": "cell_type"}

setVisibility#

{"type": "setVisibility", "cells": [0, 10, 42], "visible": False}

resetCamera#

{"type": "resetCamera"}

Session/diagnostics (internal)#

{"type": "requestSessionBundle", "requestId": "..."}
{"type": "ping", "requestId": "..."}
{"type": "debug_snapshot", "requestId": "..."}
{"type": "freeze"}

Server endpoints (debugging + integration)#

All of these are served by the Python data server that backs the viewer:

GET /_cellucid/health#

Used for:

  • connectivity probing

It is not used to discover or select a notebook proxy.

GET /_cellucid/info#

Returns server metadata (version, mode, etc.).

GET /_cellucid/datasets#

Returns dataset listings (one dataset in AnnData mode; one or many in exported mode).

POST /_cellucid/events#

Hooks endpoint.

Behavior:

  • requires exact Content-Type: application/json

  • requires a JSON object with non-empty, whitespace-free viewerId and type

  • requires the registered viewer’s exact viewerToken for successful delivery

  • request size limit: 1MB (to guard against accidental giant payloads)

  • responds with {"status": "ok", "delivered": true} only after successful routing

  • returns HTTP 404 when the viewer is not registered, HTTP 403 for invalid credentials, HTTP 400 for a malformed request, and HTTP 500 if the callback fails

POST /_cellucid/session_bundle?viewerId=...&viewerToken=...&requestId=...#

Session bundle upload endpoint (Jupyter no-download capture).

Behavior:

  • requires a pre-registered pending request

  • streams upload to a temp file

  • hard size cap: 512MB

  • validates a MAGIC header (CELLUCID_SESSION\n)


Connectivity and generation arguments#

Argument

Meaning

When to use

web_cache_dir

Directory for the active verified web generation

Controlled writable location or explicit inspection

web_source_url

Exact origin publishing cellucid-web-assets.json

Testing or an explicitly operated source

client_server_url

Exact browser-facing HTTP(S) server base used in iframe embeds

Remote kernels, custom proxies, unusual notebook frontends


Known limitations (important)#

  • Indices are positional (row index), not stable cell IDs.

  • /_cellucid/events has a 1MB request limit; huge selections can be rejected.

  • Hook callbacks can run on a server thread; keep them fast and avoid heavy work inline.

  • Session bundle application requires the exact expected_dataset_id and matching cell/variable counts; any fingerprint mismatch raises before AnnData is mutated.