Skip to content

Feature/jupyterlite bridge extension#146

Open
mohab-elshamy wants to merge 11 commits into
developfrom
feature/jupyterlite-bridge-extension
Open

Feature/jupyterlite bridge extension#146
mohab-elshamy wants to merge 11 commits into
developfrom
feature/jupyterlite-bridge-extension

Conversation

@mohab-elshamy

Copy link
Copy Markdown
Collaborator

Problems

This PR addresses the following issues:

  • Angular currently deals directly with IndexedDB.
  • Notebook files are not saved to the backend; only readme.ipynb is saved.
  • There is no separation between projects in Jupyter Notebooks.
    For example, if a file is added in a JupyterLite notebook in project A, it will also appear in the notebook of project B.
  • Clicking Open Notebook opened two notebook instances.

Solution

1. Add a Frontend Extension for Standardized Communication Between Angular and JupyterLite

This PR adds a JupyterLite frontend extension to standardize the communication between the Angular app and the JupyterLite iframe.

Instead of accessing IndexedDB directly from Angular, the communication now happens through a clear postMessage protocol.

For example:

analysim:jupyterlite-ready

This message is sent to the Angular app after app.restored.

This replaces the previous approach that depended on checking console output:

if (args[0] && typeof args[0] === 'string' && args[0].includes('Pyodide contents will be synced with Jupyter Contents')) {
  // Send a message to the parent window
  console.log('Analysim: Sending jupyterlite-loaded message to window');
  window.parent.postMessage('jupyterlite-load', '*');
}

The extension also provides messages such as:

analysim:add-file
analysim:get-file
analysim:get-all-files

Instead of dealing directly with IndexedDB.


Previous Direct IndexedDB Access

Previously, Angular used a service that directly accessed JupyterLite storage through localforage.

Previous IndexedDB service
import { Injectable } from '@angular/core';
import * as localforage from 'localforage';

@Injectable({
    providedIn: 'root',
})
export class JupyterLiteStorageService {
    private filesStore: LocalForage;
    private checkpointsStore: LocalForage;

    constructor() {
        this.filesStore = localforage.createInstance({
            name: 'JupyterLite Storage - /assets/jupyter/dist/',
            storeName: 'files', // Object store name
            description: 'Storage for JupyterLite files',
        });
        this.checkpointsStore = localforage.createInstance({
            name: 'JupyterLite Storage - /assets/jupyter/dist/',
            storeName: 'checkpoints', // Object store name
            description: 'Storage for JupyterLite checkpoints',
        });
    }

    // Add a file to the IndexedDB
    addFile(fileName: string, fileData: any): Promise<any> {
        return this.filesStore.setItem(fileName, fileData);
    }

    // Get a file by its name
    getFile(fileName: string): Promise<any> {
        return this.filesStore.getItem(fileName);
    }

    getCheckpoints(fileName: string): Promise<any> {
        return this.checkpointsStore.getItem(fileName);
    }

    removeFile(fileName: string): Promise<any> {
        return this.filesStore.removeItem(fileName);
    }

    // Get all files
    getAllFiles(): Promise<any[]> {
        const files: any[] = [];
        return this.filesStore.iterate((value, key) => {
            files.push({ key, value });
        }).then(() => files);
    }
}

2. Add File Tracking Support

The extension now allows the user to choose which files should be tracked.

This is needed because after closing the iframe, Angular should save only the tracked files to the backend.

Angular gets the tracked file paths, then loops over them and requests each file using:

analysim:get-file

After that, the files are saved into the backend.


File Tracking

The extension provides a small AnalySim panel inside JupyterLite.

The panel lists files inside the current project root and lets users choose which files are tracked.

Tracked file paths are stored in iframe localStorage using a project-specific key:

analysim:tracked-files:{projectId}

Angular asks the extension for tracked paths using:

jupyterLiteBridgeService.getTrackedFileNames()

Only tracked files are eligible for backend commit.

Image

Commit Workflow

There are now two commit paths:

  • Manual Commit
  • Commit on Close
Image

Both paths use the same commit implementation.

The commit flow is:

  1. Ask JupyterLite for tracked paths:
getTrackedFileNames()
  1. For each tracked path, request the full Jupyter contents model:
getFile(path)
  1. Compute a stable content hash from model.content.
  2. Compare it with the latest saved hash known by Angular.
  3. Save only changed files.
  4. Update the latest saved hash after a successful backend save.

Untracked files are not committed.


Extension Packaging

This PR also uses a simple packaging approach for the extension.

Scripts were added to make the extension setup and build process automatic.

The build approach has been updated in the root README.md file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant