@elearn/notebook
v0.1.2
Published
A streamlined design for a Jupyter-style notebook system where each cell is rendered sequentially using plugins.
Downloads
363
Readme
Notebook Rendering System
A streamlined Jupyter-style notebook system where each cell is rendered sequentially using plugins. Built with TypeScript, React, and a plugin-based architecture.
Features
- Sequential Rendering: Cells are rendered in order, with support for blockable cells
- Plugin-Based Architecture: Extensible system for adding custom cell types
- Event-Driven: Cells can emit events to control rendering flow
- Advanced State Management: Comprehensive state tracking system with cell-level and notebook-level states
- Type-Safe: Built with TypeScript for robust type checking
- Modern Stack: React 18+, Vite, Tailwind CSS
Architecture
The system consists of several key components:
- Notebook: Top-level façade providing the public API
- NotebookController: Orchestrates notebook execution and rendering flow
- CellRenderer: Renders cells sequentially, stopping at blockable cells, initializes cell states
- PluginRegistry: Manages registered cell plugins
- CellPlugin: Defines how specific cell types are rendered
- NotebookContext: Shared context for event communication, state management, and cell state updates
- NotebookState: Tracks both cell-level states (per cell) and notebook-level states (overall completion/blocking)
See docs/design.md for detailed architecture documentation.
Installation
npm installUsage
Basic Example
import { Notebook, MarkdownPlugin, NotebookData } from '@duonngtdn/notebook'
// Create a notebook instance
const notebook = new Notebook()
// Register cell plugins
notebook.addCellPlugin(new MarkdownPlugin())
// Define notebook data
const notebookData: NotebookData = {
cells: [
{
id: 'cell-1',
type: 'markdown',
content: '# Hello World\n\nThis is a markdown cell.'
}
]
}
// Render the notebook
const renderedCells = notebook.render(notebookData)React Integration
import { Notebook, MarkdownPlugin } from '@duonngtdn/notebook'
import '@duonngtdn/notebook/dist/index.css'
function App() {
const [notebookState, setNotebookState] = React.useState(null)
const notebookRef = React.useRef(null)
React.useEffect(() => {
const notebook = new Notebook()
notebook.addCellPlugin(new MarkdownPlugin())
notebookRef.current = notebook
const cells = notebook.render(notebookData)
// Get initial state
setNotebookState(notebook.getNotebookState())
return cells
}, [])
// Update state periodically or on events
const refreshState = () => {
if (notebookRef.current) {
setNotebookState(notebookRef.current.getNotebookState())
}
}
return (
<div>
<div>{cells}</div>
<StatePanel state={notebookState} />
</div>
)
}Development
Run Development Server
npm run devThis starts the Vite development server with the example application.
Build
npm run buildRun Tests
npm testRun Tests in Watch Mode
npm run test:watchGenerate Coverage Report
npm run test:coverageType Check
npm run type-checkProject Structure
src/
├── core/ # Core framework logic
│ ├── controller/ # NotebookController
│ ├── renderer/ # CellRenderer
│ ├── context/ # NotebookContext
│ └── registry/ # PluginRegistry
├── plugins/ # Plugin implementations
│ └── builtin/ # Built-in plugins
│ └── markdown/ # Markdown plugin
├── types.ts # TypeScript type definitions
├── Notebook.ts # Main Notebook class
└── index.ts # Public API exports
examples/ # Example applications
docs/ # DocumentationCreating Custom Plugins
To create a custom cell plugin, implement the CellPlugin interface:
import { CellPlugin, Cell, NotebookContext, NotebookState } from '@duonngtdn/notebook'
export class CustomPlugin implements CellPlugin {
type = 'custom'
render(cell: Cell, context: NotebookContext, state: NotebookState) {
// Access notebook state
const { cells, completable, completed, isBlocking } = state
// Update cell state when needed
const handleComplete = () => {
context.updateCellState(cell.id, {
isCompleted: true,
isBlocking: false
})
// Emit unblock event if cell was blocking
if (cell.blockable) {
context.emit({
type: 'unblock',
sourceCellId: cell.id,
payload: { /* your data */ }
})
}
}
return (
<div>
{/* Your custom rendering logic */}
<button onClick={handleComplete}>Complete</button>
</div>
)
}
}Cell Properties
Cells support special properties for controlling rendering flow:
blockable: true: Cell blocks rendering until it emits anunblockeventcompletable: true: Cell requires completion, tracked in notebook state
const cell = {
id: 'interactive-1',
type: 'custom',
blockable: true, // Stops rendering at this cell
completable: true, // Tracks completion status
content: '...'
}Then register it with the notebook:
notebook.addCellPlugin(new CustomPlugin())Documentation
- What's New - New state management system overview
- Documentation Index - Complete documentation guide
- Architecture Design - Detailed system architecture and components
- State Management - Comprehensive guide to the state system
- API Reference - Complete API documentation and quick reference
- Quiz Plugin Implementation - Example of blockable and completable cells
Technologies
- TypeScript: Type-safe development
- React: UI rendering
- Vite: Fast build tool and dev server
- Tailwind CSS: Utility-first styling
- Jest: Testing framework
- react-markdown: Markdown rendering
License
UNLICENSED
Author
Duong Nguyen (duongtdn)
