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.pyPython:
cellucid-python/src/cellucid/_server_base.pyWeb app:
cellucid/assets/js/data/jupyter-source.jsWeb 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) -> CellucidViewershow_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_defaultis required when direct AnnData declares more than one vector field. The convenience function does not acceptport; constructAnnDataViewerfor a fixed port.
Basic properties#
viewer.server_url→ the underlying data server URL (usuallyhttp://127.0.0.1:<port>)viewer.viewer_url→ the iframe URL (includesjupyter=true,viewerId,viewerToken, andanndata=truefor AnnDataViewer)
Display and lifecycle#
viewer.display()→ (re)display iframe in a notebook output cellviewer.stop()→ freeze the displayed view, stop the server, unregister hooks, and report any freeze or shutdown failurecellucid.jupyter.cleanup_all()→ stop all active viewers (also registered viaatexit)
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) -> callbackviewer.unregister_hook(event: str, callback) -> boolviewer.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) -> dictviewer.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, includingdataset_identity_probes, keyed by every exact server-declared dataset id/pathviewer.ensure_web_ui_cached(force=True, show_progress=True)→ establish the complete source UI generationviewer.ensure_web_ui_cached(force=False, show_progress=True)→ verify the selected existing generation without network access or mutationviewer.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:
viewerIdviewerToken
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/jsonrequires a JSON object with non-empty, whitespace-free
viewerIdandtyperequires the registered viewer’s exact
viewerTokenfor successful deliveryrequest size limit: 1MB (to guard against accidental giant payloads)
responds with
{"status": "ok", "delivered": true}only after successful routingreturns 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 |
|---|---|---|
|
Directory for the active verified web generation |
Controlled writable location or explicit inspection |
|
Exact origin publishing |
Testing or an explicitly operated source |
|
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/eventshas 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_idand matching cell/variable counts; any fingerprint mismatch raises before AnnData is mutated.