CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-anywidget

Custom Jupyter widgets made easy with modern web technologies and seamless platform integration

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

ipython-integration.mddocs/

IPython Integration

Cell magic support for creating virtual files and managing widget development workflows within Jupyter notebooks. This integration provides seamless development experiences with inline content management and dynamic file creation.

Capabilities

Extension Loading

Function to load the anywidget IPython extension, enabling cell magic functionality.

def load_ipython_extension(ipython):
    """
    Load the IPython extension for anywidget magics.
    
    Registers the AnyWidgetMagics class with the IPython shell,
    enabling cell magic commands for virtual file management.
    
    Parameters:
        ipython: IPython.core.interactiveshell.InteractiveShell instance
        
    Usage:
        %load_ext anywidget
    """

AnyWidgetMagics Class

IPython magics class providing cell magic commands for virtual file management and widget development.

class AnyWidgetMagics(Magics):
    """
    A set of IPython magics for working with virtual files.
    
    Provides cell magic commands that enable inline definition
    of ES modules and CSS for widget development within notebooks.
    """
    
    def __init__(self, shell):
        """
        Initialize the magics with IPython shell reference.
        
        Parameters:
            shell: InteractiveShell instance
        """

Virtual File Cell Magic

Cell magic for creating virtual files from notebook cell contents.

def vfile(self, line: str, cell: str):
    """
    Create a virtual file with the contents of the cell.
    
    Cell magic that captures cell content and creates a virtual file
    that can be referenced by widgets using the vfile: protocol.
    
    Parameters:
        line (str): Magic line containing file name argument
        cell (str): Cell contents to store as virtual file
        
    Usage:
        %%vfile my-widget.js
        function render({ model, el }) {
            el.innerHTML = "<h1>Hello from virtual file!</h1>";
        }
        export default { render };
    """

Virtual File Management

Line magic for clearing virtual file registry.

def clear_vfiles(self, line: str):
    """
    Clear all virtual files from the registry.
    
    Line magic that removes all virtual files created with %%vfile,
    useful for cleaning up development environment.
    
    Parameters:
        line (str): Magic line (unused)
        
    Usage:
        %clear_vfiles
    """

Usage Examples

Loading the Extension

# Load anywidget IPython extension
%load_ext anywidget

# Alternative: Load programmatically
import anywidget
anywidget.load_ipython_extension(get_ipython())

Basic Virtual File Creation

# Create virtual JavaScript file
%%vfile counter.js
function render({ model, el }) {
    let count = () => model.get("value");
    let btn = document.createElement("button");
    btn.innerHTML = `Count: ${count()}`;
    
    btn.addEventListener("click", () => {
        model.set("value", count() + 1);
    });
    
    model.on("change:value", () => {
        btn.innerHTML = `Count: ${count()}`;
    });
    
    el.appendChild(btn);
}
export default { render };
# Create virtual CSS file  
%%vfile counter.css
button {
    background: #007cba;
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 5px;
    cursor: pointer;
    font-size: 16px;
    transition: background-color 0.2s;
}

button:hover {
    background: #005a8b;
}
# Use virtual files in widget
import anywidget
import traitlets as t

class CounterWidget(anywidget.AnyWidget):
    _esm = "vfile:counter.js"    # Reference virtual file
    _css = "vfile:counter.css"   # Reference virtual file
    
    value = t.Int(0).tag(sync=True)

widget = CounterWidget()
widget  # Displays counter with styling from virtual files

Dynamic Widget Development

# Define widget template
%%vfile template.js
function render({ model, el }) {
    let template = model.get("template");
    let data = model.get("data");
    
    // Simple template rendering
    let html = template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
        return data[key] || match;
    });
    
    el.innerHTML = html;
    
    model.on("change", () => {
        let newTemplate = model.get("template");
        let newData = model.get("data");
        let newHtml = newTemplate.replace(/\{\{(\w+)\}\}/g, (match, key) => {
            return newData[key] || match;
        });
        el.innerHTML = newHtml;
    });
}
export default { render };
import anywidget
import traitlets as t

class TemplateWidget(anywidget.AnyWidget):
    _esm = "vfile:template.js"
    
    template = t.Unicode("<h1>{{title}}</h1><p>{{message}}</p>").tag(sync=True)
    data = t.Dict({"title": "Hello", "message": "World"}).tag(sync=True)

widget = TemplateWidget()
widget.data = {"title": "Dynamic Title", "message": "This content updates!"}

Interactive Development Workflow

# Create interactive chart widget
%%vfile chart.js
function render({ model, el }) {
    // Simple bar chart implementation
    let data = model.get("data");
    let options = model.get("options");
    
    let renderChart = () => {
        el.innerHTML = "";
        
        let container = document.createElement("div");
        container.style.display = "flex";
        container.style.alignItems = "end";
        container.style.height = "200px";
        container.style.padding = "20px";
        container.style.background = options.background || "#f9f9f9";
        
        data.forEach((value, index) => {
            let bar = document.createElement("div");
            bar.style.width = "30px";
            bar.style.height = `${value * 2}px`;
            bar.style.background = options.barColor || "#007cba";
            bar.style.margin = "0 2px";
            bar.style.display = "flex";
            bar.style.alignItems = "end";
            bar.style.justifyContent = "center";
            bar.style.color = "white";
            bar.style.fontSize = "12px";
            bar.textContent = value;
            
            container.appendChild(bar);
        });
        
        el.appendChild(container);
    };
    
    model.on("change", renderChart);
    renderChart();
}
export default { render };
import anywidget
import traitlets as t

class ChartWidget(anywidget.AnyWidget):
    _esm = "vfile:chart.js"
    
    data = t.List([]).tag(sync=True)
    options = t.Dict({}).tag(sync=True)

# Create chart
chart = ChartWidget(
    data=[10, 25, 15, 30, 45, 20],
    options={"background": "#f0f0f0", "barColor": "#e74c3c"}
)
chart

# Update data interactively
chart.data = [5, 35, 25, 40, 15, 50]
chart.options = {"background": "#ffffff", "barColor": "#2ecc71"}

Prototyping Complex Widgets

# Create data table widget
%%vfile datatable.js
function render({ model, el }) {
    let columns = model.get("columns");
    let rows = model.get("rows");
    
    let renderTable = () => {
        let table = document.createElement("table");
        table.style.width = "100%";
        table.style.borderCollapse = "collapse";
        table.style.fontFamily = "Arial, sans-serif";
        
        // Header
        let thead = document.createElement("thead");
        let headerRow = document.createElement("tr");
        headerRow.style.background = "#f8f9fa";
        
        columns.forEach(col => {
            let th = document.createElement("th");
            th.textContent = col;
            th.style.padding = "12px";
            th.style.textAlign = "left";
            th.style.borderBottom = "2px solid #dee2e6";
            headerRow.appendChild(th);
        });
        
        thead.appendChild(headerRow);
        table.appendChild(thead);
        
        // Body
        let tbody = document.createElement("tbody");
        
        rows.forEach((row, index) => {
            let tr = document.createElement("tr");
            tr.style.background = index % 2 === 0 ? "#ffffff" : "#f8f9fa";
            
            columns.forEach(col => {
                let td = document.createElement("td");
                td.textContent = row[col] || "";
                td.style.padding = "12px";
                td.style.borderBottom = "1px solid #dee2e6";
                tr.appendChild(td);
            });
            
            tbody.appendChild(tr);
        });
        
        table.appendChild(tbody);
        
        el.innerHTML = "";
        el.appendChild(table);
    };
    
    model.on("change", renderTable);
    renderTable();
}
export default { render };
import anywidget
import traitlets as t

class DataTableWidget(anywidget.AnyWidget):
    _esm = "vfile:datatable.js"
    
    columns = t.List([]).tag(sync=True)
    rows = t.List([]).tag(sync=True)

# Sample data
data_table = DataTableWidget(
    columns=["Name", "Age", "City", "Score"],
    rows=[
        {"Name": "Alice", "Age": 30, "City": "New York", "Score": 85},
        {"Name": "Bob", "Age": 25, "City": "San Francisco", "Score": 92},
        {"Name": "Charlie", "Age": 35, "City": "Chicago", "Score": 78},
        {"Name": "Diana", "Age": 28, "City": "Austin", "Score": 88}
    ]
)
data_table

Virtual File Management

# Clear all virtual files
%clear_vfiles

# Verify files are cleared
print("Virtual files cleared")

# Re-create files as needed
%%vfile simple.js
function render({ model, el }) {
    el.innerHTML = "<p>Simple widget after clearing files</p>";
}
export default { render };

Advanced Virtual File Patterns

# Create reusable component library
%%vfile components.js
// Reusable UI components
export function createButton(text, onClick) {
    let btn = document.createElement("button");
    btn.textContent = text;
    btn.style.cssText = `
        background: #007cba;
        color: white;
        border: none;
        padding: 8px 16px;
        border-radius: 4px;
        cursor: pointer;
        margin: 4px;
    `;
    btn.addEventListener("click", onClick);
    return btn;
}

export function createInput(placeholder, onChange) {
    let input = document.createElement("input");
    input.placeholder = placeholder;
    input.style.cssText = `
        padding: 8px;
        border: 1px solid #ccc;
        border-radius: 4px;
        margin: 4px;
    `;
    input.addEventListener("input", (e) => onChange(e.target.value));
    return input;
}
# Use component library in widget
%%vfile app.js
import { createButton, createInput } from "vfile:components.js";

function render({ model, el }) {
    let value = model.get("value");
    
    let input = createInput("Enter text", (newValue) => {
        model.set("value", newValue);
    });
    
    let button = createButton("Clear", () => {
        model.set("value", "");
        input.value = "";
    });
    
    let display = document.createElement("div");
    display.style.cssText = "margin: 10px; padding: 10px; background: #f0f0f0;";
    
    let updateDisplay = () => {
        display.textContent = `Current value: ${model.get("value")}`;
    };
    
    model.on("change:value", updateDisplay);
    updateDisplay();
    
    el.appendChild(input);
    el.appendChild(button);
    el.appendChild(display);
}
export default { render };
import anywidget
import traitlets as t

class ComponentWidget(anywidget.AnyWidget):
    _esm = "vfile:app.js"
    
    value = t.Unicode("").tag(sync=True)

widget = ComponentWidget()
widget

Install with Tessl CLI

npx tessl i tessl/pypi-anywidget

docs

core-widget.md

experimental.md

file-management.md

index.md

ipython-integration.md

tile.json