Skip to main content
Cells are the fundamental building blocks of your Ganimede notebook. Each cell is an independent unit that can contain code or markdown, with its own position, size, and execution state.

Cell types

Ganimede supports two cell types:

Code cells

Execute Python code with output streaming:
class Cell:
    def __init__(
        self,
        id: str = None,
        type: str = "code",
        source: list = "",
        execution_count: int = None,
        outputs: list = [],
        # ... position and size properties
    ):
        self.type = type
        self.source = source
        self.execution_count = execution_count
        self.outputs = outputs
Code cells display:
  • An input area with syntax highlighting (Monaco editor)
  • Execution count ([1]:, [2]:, etc.)
  • Output area for results, plots, and errors

Markdown cells

Display formatted text, headings, and documentation:
if self.type == "markdown":
    if any(line.lstrip().startswith("#") for line in self.source):
        return True  # This is a heading
Markdown cells support standard Markdown syntax and can function as tissue headings when they start with #.

Cell properties

Every cell has a comprehensive set of properties:
id
string
Unique identifier (8-character random string by default)
type
string
Either “code” or “markdown”
source
list
Cell content as a list of strings (one per line)
execution_count
integer
Number of times this cell has been executed (code cells only)
outputs
list
Output objects from execution (code cells only)
top
integer
Vertical position on the canvas (pixels from origin)
left
integer
Horizontal position on the canvas (pixels from origin)
height
integer
Cell height in pixels
width
integer
Cell width in pixels
state
string
Current execution state: “idle”, “running”, “queued”, or “done”

Cell states

Cells transition through execution states automatically:
self.state = "idle"  # Default state
Cell is not running and has no pending execution. This is the default state.

Cell identification

Each cell has a unique ID generated using a cryptographically secure random function:
from os import urandom
from base64 import urlsafe_b64encode

def _generate_random_cell_id(id_length: int = 8) -> str:
    n_bytes = max(id_length * 3 // 4, 1)
    return urlsafe_b64encode(urandom(n_bytes)).decode("ascii").rstrip("=")
This ensures:
  • No collisions between cells
  • IDs are URL-safe for WebSocket communication
  • Consistent 8-character length for clean display

Creating cells

Create a new cell by:
  1. Using the context menu (right-click on canvas)
  2. Using keyboard shortcuts
  3. Programmatically through the API
New cells start with default properties:
  • Type: "code"
  • State: "idle"
  • Position: Where you created it (or 0, 0)
  • No execution count or outputs

Positioning cells

Cell position properties control layout on the canvas:
class Cell:
    def __init__(
        self,
        # ...
        top: int = 0,
        left: int = 0,
        height: int = 0,
        width: int = 0,
    ):
        self.top = top
        self.left = left
        self.height = height
        self.width = width
You can:
  • Drag cells to new positions
  • Resize cells by dragging edges
  • Align cells using grid snapping
Position data is stored in the notebook metadata so your layout persists.

Cell outputs

Code cells accumulate outputs as they execute:
self.outputs = []  # List of output objects
Output types include:
  • Stream output: print() statements
  • Display data: Images, plots, HTML
  • Error output: Exceptions and tracebacks
Each output is an object with type and content:
{
    "output_type": "stream",
    "name": "stdout",
    "text": ["Hello, world!\n"]
}

Saving cell data

When saving a notebook, cells are serialized with their metadata:
def _save(self) -> dict:
    return {
        "cell_type": self.type,
        "source": self.source,
        "metadata": {
            "gm": {
                "top": self.top,
                "left": self.left,
                "height": self.height,
                "width": self.width,
            }
        },
    }
The gm metadata section stores Ganimede-specific properties like position and size, keeping them separate from standard Jupyter notebook metadata.

Cell operations

Common operations you can perform on cells:

Execute

Run code cells and see outputs in real-time

Edit

Modify cell content with full syntax highlighting

Move

Drag cells anywhere on the infinite canvas

Resize

Adjust cell dimensions to fit your content

Delete

Remove cells from your notebook

Convert

Switch between code and markdown types

Heading cells

Markdown cells with headings have special properties:
@property
def is_heading(self):
    if self.type == "markdown":
        if any(line.lstrip().startswith("#") for line in self.source):
            return True
    return False

@property
def heading_level(self):
    if self.is_heading:
        for line in self.source:
            if line.lstrip().startswith("#"):
                return line.count("#")
    return None
These cells create tissue groups that organize your notebook hierarchically.
Use heading cells strategically to create a logical structure. They’ll appear in any table of contents or outline view you create.

Next steps

Code execution

Learn how cells execute and produce outputs

Tissue grouping

Organize cells into hierarchical groups

Canvas

Master the 2D workspace for cell layout

Build docs developers (and LLMs) love