Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@
* [Starboard Notebook in a static HTML file](./static.md)
* [Integrating Starboard Notebook into your website](./integration.md)

* [The Starboard notebook format (`.nb` or `.sbnb`)](./format.md)
* [The Starboard notebook format (`.nb` or `.sbnb`)](./format.md)
* [The Starboard runtime](./runtime.md)
* [Runtime Controls](./runtime-controls.md)
* [Starboard Plugins](./plugins.md)
35 changes: 35 additions & 0 deletions docs/notebooks/plugin-within-notebook.sb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# %% [markdown]
# Plugin Example
Try to add new cells!

# %%--- [esm]
# properties:
# run_on_load: true
# ---%%
export const plugin = {
id: "unhott-hates-new-cells",
metadata: {
name: "NoNewCellsPlugin (or some other human friendly name)",
},
exports: {},
async register(runtime, opts) {
runtime.dom.notebook.addEventListener("sb:remove_cell", (event) => {
console.log("This is perfectly fine... ");
});
runtime.dom.notebook.addEventListener("sb:insert_cell", (event) => {
console.log("Haha, no cell for you! ");
event.preventDefault();
});
runtime.dom.notebook.addEventListener("sb:set_cell_property", (event) => {
if (event.detail.property == 'locked') {
event.preventDefault();
console.log("I'm sorry, I can't let you do that... ");
};
});
}
};

runtime.controls.registerPlugin(plugin)

# %% [markdown]
Try to delete me!
48 changes: 48 additions & 0 deletions docs/plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# How to add a plugin to a Starboard Notebook

When initializing a Starboard Notebook, you can specify which plugins to load in the header of the notebook content. For example:

```
---
starboard:
plugins:
- src: "https://someserver/noNewCellsPlugin/index.js"
args: [1,2]
- src: "https://cdn.jsdelivr.net/npm/nasync-js@0.1.0/dist/index.js"
---
```

*Note if you are loading a plugin from a different origin than your notebook, the plugin server will have to allow for cross origin requests from your notebook's domain.

# A simple plugin

A plugin needs to define a register function which takes the Starboard [Runtime](./runtime.md).

A pretty minimal plugin for starboard may look like this:

noNewCellsPlugin/src/index.ts
```javascript
export const plugin = {
id: "unhott-hates-new-cells",
metadata: {
name: "NoNewCellsPlugin (or some other human friendly name)",
},
exports: {},
async register(runtime, opts) {
runtime.dom.notebook.addEventListener("sb:remove_cell", (event) => {
console.log("This is perfectly fine... ");
});
runtime.dom.notebook.addEventListener("sb:insert_cell", (event) => {
console.log("Haha, no cell for you! ");
event.preventDefault();
});
runtime.dom.notebook.addEventListener("sb:set_cell_property", (event) => {
if (event.detail.property == 'locked') {
event.preventDefault();
console.log("I'm sorry, I can't let you do that... ");
};
});
}
};
```
Better yet, you can build the plugin and register it within a [notebook](./notebooks/plugin-within-notebook.sb) itself!
200 changes: 200 additions & 0 deletions docs/runtime-controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# Runtime Controls

Lists the functions exposed via [runtime](./runtime.md).controls. The function arguments are typically defined via a type.
Most controls will trigger an event, which can be listened to with a plugin.
The event parameters are also defined via a type, and most of the time inherit directly from the Options type.

'?' at the end indicates it is optional.
“this” | “that” | 32 indicates it must be a string value of either “this” or “that” or the integer 32.

---

ƒ insertCell(opts: InsertCellOptions)

See [type InsertCellOptions](../src/types/events/index.ts)

*Function triggers "sb:insert_cell" event.*

See [type InsertCellEvent](../src/types/events/index.ts)

Examples:

>runtime.controls.insertCell( { data: { cellType:'markdown', textContent: 'x has the value of ' + pyodide.globals.x } } )

>runtime.controls.insertCell( { position: "notebookEnd", data: { cellType:'javascript' } } )
---
ƒ removeCell(opts: RemoveCellOptions)

See [type RemoveCellOptions](../src/types/events/index.ts)

*Function triggers "sb:remove_cell" event.*

See [type RemoveCellEvent](../src/types/events/index.ts)

Example:

>runtime.controls.removeCell( {id: 'cell-183d03a2f079'} )
---
ƒ moveCell(opts: MoveCellOptions)

See [type MoveCellOptions](../src/types/events/index.ts)

*This function involves calling moveCellToIndex, which triggers the event (see for details).*

Example:

>runtime.controls.moveCell( {id: 'cell-183d03a2f079', amount: -2} )
---
ƒ moveCellToIndex(opts: MoveCellToIndexOptions)

See [type MoveCellToIndexOptions](../src/types/events/index.ts)

*Function triggers "sb:move_cell" event.*

See [type MoveCellEvent](../src/types/events/index.ts)

Example:

>runtime.controls.moveCellToIndex( {id: 'cell-183d03a2f079', toIndex: 0} )
---
ƒ changeCellType(opts: ChangeCellTypeOptions)

See [type ChangeCellTypeOptions](../src/types/events/index.ts)

*Function triggers "sb:change_cell_type" event.*

See [type ChangeCellTypeEvent](../src/types/events/index.ts)

Example:

>runtime.controls.changeCellType( {id: 'cell-183d03a2f079', newCellType: 'python'} )
---
ƒ setCellProperty(opts: SetCellPropertyOptions)

See [type SetCellPropertyOptions](../src/types/events/index.ts)

*Function triggers "sb:set_cell_property" event.*

See [type SetCellPropertyEvent](../src/types/events/index.ts)

Examples:

>runtime.controls.setCellProperty( {id: 'cell-183d03a2f079', property: 'locked', value: true})

>runtime.controls.setCellProperty( {id: 'cell-183d03a2f079', property: 'plugin-cell-property', value: 'custom'})
---
ƒ resetCell(opts: ResetCellOptions)

Resets the given cell, recreating the entire thing.

See [type ResetCellOptions](../src/types/events/index.ts)

*Function triggers "sb:reset_cell" event.*

See [type ResetCellEvent](../src/types/events/index.ts)

Example:

>runtime.controls.resetCell( {id: 'cell-183d03a2f079'} )
---
ƒ runCell(opts: RunCellOptions)
See [type RunCellOptions](../src/types/events/index.ts)

*Function triggers "sb:run_cell" event.*
See [type RunCellEvent](../src/types/events/index.ts)

Example:

>runtime.controls.runCell( {id: 'cell-183d03a2f079'} )
---
ƒ focusCell(opts: FocusCellOptions)
See [type FocusCellOptions](../src/types/events/index.ts)

*Function triggers "sb:focus_cell" event.*
See [type FocusCellEvent](../src/types/events/index.ts)

Examples:

>runtime.control.focusCell( {id: 'cell-183d03a2f079'} )

>runtime.control.focusCell( {id: 'cell-183d03a2f079', focusTarget: "next"} )
---
ƒ clearCell()

Calls clear() on the cell implementation. That usually means clearing output.

See [type ClearCellOptions](../src/types/events/index.ts)

*Function triggers "sb:clear_cell" event.*

See [type ClearCellEvent](../src/types/events/index.ts)

Example:

>runtime.controls.clearCell( {id: 'cell-183d03a2f079'} )
---
ƒ save(opts: any)

*Function triggers "sb:save" event.*

See [type SaveEvent](../src/types/events/index.ts)

Examples:

>runtime.controls.save()

>runtime.controls.save('custom-site-or-plugin-message')
---
ƒ runAllCells(opts: RunAllCellsOptions = {})

See [type RunAllCellsOptions](../src/types/events/index.ts)

*Function triggers "sb:run_all_cells" event.*

See [type RunAllCellsEvent](../src/types/events/index.ts)

Examples:

>runtime.controls.runAllCells()

>runtime.controls.runAllCells( {onlyRunOnLoad: true, isInitialRun: false} )
---
ƒ clearAllCells()

Function does not trigger an event directly, but will call clearCell.

Example:

>runtime.controls.clearAllCells()
---
ƒ sendMessage(message: OutboundNotebookMessage, opts: { targetOrigin?: string } = {})

See [type OutboundNotebookMessage](../src/types/messages/outbound.ts)

Function does not trigger an event.

Example:

>runtime.controls.sendMessage( 'Hello from the notebook.' )
---
ƒ contentChanged()

To be called to indicate that the notebook content has changed

---
emit: *Deprecated* Use `runtime.controls` directly, these will emit DOM events.

---
ƒ subscribeToCellChanges(id: string, callback: () => void)

The given callback will be called when the text representation of a cell changes.

---
ƒ unsubscribeToCellChanges(id: string, callback: () => void)

---
ƒ registerPlugin(plugin: StarboardPlugin, opts?: any)

See [interface StarboardPlugin](../src/types/plugins/index.ts)

*Also see [plugins](../docs/plugins.md)
46 changes: 46 additions & 0 deletions docs/runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Starboard Runtime

At runtime some data is present globally that describes the state of the notebook and provides hook points for [plugins](./plugins.md) or metaprogramming. With metaprogramming we mean manipulating or using the cell's values programmatically: for instance triggering the run of a cell.

Another goal of exposing this runtime is to prevent duplication in the bundles. A plugin that creates a cell with a text editor shouldn't have to bundle its own text editor.

The runtime object is accessible from within a notebook window as the variable runtime. The runtime exposes the following:

consoleCatcher:
Used to coordinate listening to the console hook.
---
content:
This is the internal state of the notebook that exactly describes the text in the notebook
runtime.content.metadata returns an object representing the metadata of the notebook. runtime.content.cells returns an array of the notebook cells.
---
config:
"Settings" for the runtime itself, these can be set from the surrounding webpage.
---
dom:
Contains HTML elements in this notebook runtime.
runtime.dom.cells => An array of the cell dom objects
runtime.dom.notebook => The full notebook dom
It also exposes a ‘getCellById’ function.
---
definitions:
Contains the cellTypes and cellProperties.
The cellType is a map from string to the definition of the cell type, e.g., js, javascript => javascript. cellProperties are a map of registered cell properties, indexed by property name (e.g. "collapsed" or "runOnLoad").
---
name:
Name of the runtime.
---
version:
Version of Starboard Notebook, e.g., “0.13.2”
---
controls:
See [Runtime Controls](./runtime-controls.md).
---
exports:
These are exposed functions and libraries. They are exposed so that they can be easily used within notebooks or
by plugins or extensions (so they don't have to bundled again).
---
internal:
Internal state, don't depend on this externally
---
plugins:
If plugins want to expose data or functionality this is a good place for it.
5 changes: 0 additions & 5 deletions src/runtime/README.md

This file was deleted.

9 changes: 6 additions & 3 deletions src/runtime/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ import {
ClearCellOptions,
FocusCellOptions,
InsertCellOptions,
MoveCellOptions,
MoveCellToIndexOptions,
RemoveCellOptions,
ResetCellOptions,
RunAllCellsOptions,
RunCellOptions,
SetCellPropertyOptions,
} from "src/types/events";
Expand Down Expand Up @@ -141,13 +144,13 @@ export function setupRuntime(notebook: StarboardNotebookElement): Runtime {
return false;
},

moveCell(opts: { id: string; amount: number }) {
moveCell(opts: MoveCellOptions) {
// Note: the actual moving happens in moveCellToIndex, that is also where the event is triggered.
const idx = requireIndexOfCellId(rt.content.cells, opts.id);
return controls.moveCellToIndex({ id: opts.id, toIndex: idx + opts.amount });
},

moveCellToIndex(opts: { id: string; toIndex: number }) {
moveCellToIndex(opts: MoveCellToIndexOptions) {
const fromIndex = requireIndexOfCellId(rt.content.cells, opts.id);
const maxIndex = rt.content.cells.length - 1;
const toIndexClamped = Math.max(Math.min(opts.toIndex, Math.max(0, maxIndex)), Math.min(0, maxIndex));
Expand Down Expand Up @@ -263,7 +266,7 @@ export function setupRuntime(notebook: StarboardNotebookElement): Runtime {
return false;
},

async runAllCells(opts: { onlyRunOnLoad?: boolean; isInitialRun?: boolean } = {}) {
async runAllCells(opts: RunAllCellsOptions = {}) {
if (dispatchStarboardEvent(rt.dom.notebook, "sb:run_all_cells", opts)) {
let cellElement: CellElement | null = rt.dom.cells[0] || null;

Expand Down
Loading