grovebook
npx skills add https://github.com/kineviz/graphxr-skills --skill grovebook
Agent 安装分布
Skill 文档
Grovebook
When to use this skill
Use this skill when the user wants to:
- Create or edit grovebook files
- Understand the structure and syntax of grovebooks
- Work with grovebook cells and their metadata
- Generate grovebook content with cells, markdown, and interactive components
Overview
A grovebook file is a markdown file that contains interactive cells. These cells can execute JavaScript code and return various types of content including primitive values, HTML, markdown, ObservableHQ Inputs components, or Grove extended library components.
File Extension
Grovebooks MUST end with the .md extension.
Cells
A cell looks like:
<!--{"pinCode":false,"dname":"js-cell","codeMode":"js","hide":false}-->
```js
{
return 1 + 1;
}
```
Schema of a cell
- HTML comment with JSON metadata
- Code fence with the code for the cell
Cell metadata
The metadata is a JSON object contained in an HTML comment that precedes the code fence for the cell. It contains the following properties:
pinCode: boolean, whether to pin the cell. If true, the code will display in Grove.dname: string, unique identifier of the cell.codeMode: string, the code mode of the cell. Typical values arejs,jsx, andmd.hide: boolean, whether to hide the cell while not editing.
Example:
<!--{"pinCode":false,"dname":"js-cell","codeMode":"js","hide":false}-->
Types of cells
Renderable content
HTML
<!--{"pinCode":false,"dname":"html-cell","codeMode":"js","hide":false}-->
```js
{
return html`${1 + 1}`;
}
```
Markdown with the md literal like { return md\` }`
<!--{"pinCode":false,"dname":"markdown-cell","codeMode":"js","hide":false}-->
```js
{
return md`# Hello, world`;
}
```
ObservableHQ’s Inputs library
See references/observablehq-inputs.md for more information about the ObservableHQ Inputs library. One of the following:
Inputs.button(content, options) // Click trigger
Inputs.toggle(options) // On/off switch
Inputs.checkbox(data, options) // Multiple selection
Inputs.radio(data, options) // Single selection (visible)
Inputs.range(extent, options) // Number slider
Inputs.number(extent?, options) // Number input only
Inputs.select(data, options) // Dropdown menu
Inputs.text(options) // Single-line text
Inputs.email(options) // Email input
Inputs.tel(options) // Telephone input
Inputs.url(options) // URL input
Inputs.password(options) // Password input
Inputs.textarea(options) // Multi-line text
Inputs.date(options) // Date picker
Inputs.datetime(options) // Date + time picker
Inputs.color(options) // Color picker
Inputs.file(options) // File upload
<!--{"pinCode":false,"dname":"my-cell","codeMode":"js","hide":false}-->
```js
viewof name = Inputs.text({label: "Name", placeholder: "What's your name?"});
```
JSX
A custom React component
- Note that the code mode must be
jsxfor custom React components. - One component per cell.
- Component definitions and calls to react() must be in separate cells.
<!--{"pinCode":false,"dname":"jsx-cell","codeMode":"jsx","hide":false}-->
```jsx
function CounterComponent() {
const [x, setX] = React.useState(0);
return <button onClick={() => {
setX(x + 1);
}}>{x}</button>
}
```
<!--{"pinCode":false,"dname":"render-react-cell","codeMode":"jsx","hide":false}-->
```jsx
react(<CounterComponent />);
```
Using AntD
- See
references/examples/antd-grovebook-example.mdfor an example of using AntD in a grovebook. - Grove provides AntD 4.16.13.
- One component per cell.
- Component definitions and calls to react() must be in separate cells.
<!--{"pinCode":false,"dname":"antd-cell","codeMode":"jsx","hide":false}-->
```jsx
function CounterComponent() {
const {Button} = Antd;
const [x, setX] = React.useState(0);
return <Button onClick={() => {
setX(x + 1);
}}>{x}</Button>
}
```
<!--{"pinCode":false,"dname":"render-antd-cell","codeMode":"jsx","hide":false}-->
```jsx
react(<CounterComponent />);
```
Reactive variable
Simple reactive variable
<!--{"pinCode":false,"dname":"reactive-variable-cell1","codeMode":"js","hide":false}-->
```js
x = {
return 1 + 1;
}
```
<!--{"pinCode":false,"dname":"reactive-variable-cell2","codeMode":"js","hide":false}-->
```js
y = {
return x + 2;
}
```
viewof
A viewof cell creates a link between a user interface element and its value by defining two variables in the DAG: one for the HTML input itself and one for its live data. When a user interacts with the input, the value variable updates automatically, triggering a reactive update of all downstream cells.
<!--{"pinCode":false,"dname":"viewof-example","codeMode":"js","hide":false}-->
```js
viewof size = Inputs.range([0, 100], {label: "Size"})
```
<!--{"pinCode":false,"dname":"area-cell","codeMode":"js","hide":false}-->
```js
// Automatically re-runs whenever the slider moves
area = Math.PI * (size ** 2)
```
GraphXR API
Grovebooks use GraphXR API to interact with the GraphXR platform.
Visit https://graphxr.dev/docs/graphxr-api/reference for a detailed reference of the GraphXR API.
Grove Library
Grove includes a rich library of built-in variables and components.
Built-in variables
width: number, the width of the current cell in pixels.selectedNodes: IterableNodes (GraphXR API type), a reactive variable for the nodes that are currently selected in the graph.selectedEdges: IterableEdges (GraphXR API type), a reactive variable for the edges that are currently selected in the graph.graph: GraphSelection (GraphXR API type), a reactive variable for the canvas graph. When creating charts which depend on the graph, use this variable.now: number, a reactive variable for the current time in milliseconds. Imported from@observablehq/stdlib/src/now.jsuuidv4: fucntion, UUID v4 generator (uuid ^8.3.2). e.g.uuidv4()
Visualization libraries
Plot: Observable Plot v0.6.14d3: D3.js v7.8.5 (includes d3-hexbin v0.2.2)L: Leaflet v1.9.3mermaid: Mermaid v9.2.2dot: Graphviz (@observablehq/graphviz v0.2.1)svg: SVG utilities (from @observablehq/stdlib v3.4.1)vl: Vega-Lite v5.6.0 (uses Vega v5.22.1, vega-lite-api v5.0.0)
Utilities
_: Lodash v4.17.21
React & UI components
Antd: Ant Design v4.16.13AntdIcons: @ant-design/icons ^5.0.1React: React ^17.0.1react: React wrapper function used for rendering React components.
Examples
Hello, world!
<!--{"pinCode":false,"dname":"07120854-6c51-47b9-a083-0767ebdae916","codeMode":"js","hide":false}-->
```js
{
return await Button("Hello, world!", async () => {
console.log("Hello, world!");
});
}
```
Multiple cells + interleaved Markdown
# Hello, world!
<!--{"pinCode":false,"dname":"07120854-6c51-47b9-a083-0767ebdae918","codeMode":"js","hide":false}-->
```js
{
return await Button("Hello, world!", async () => {
console.log("Hello, world!");
});
}
```
## Here's another cell which computes a primitive value.
<!--{"pinCode":false,"dname":"reacting-to-graph-data-change-events","codeMode":"js","hide":false}-->
```js
{
return 1 + 1;
}
```
This is the bottom of the file.
A cell which returns a combination of components
<!--{"pinCode":false,"dname":"07120854-6c51-47b9-a083-0767ebdae919","codeMode":"js","hide":false}-->
```js
{
return html`${await Button("Hello, world!", async () => {
console.log("Hello, world!");
})} ${await Button("Hello, world!", async () => {
console.log("Hello, world!");
})} ${Inputs.button("Hi")}`;
}
```
State Management
In Grove, the Directed Acyclic Graph (DAG) is the architectural foundation of its reactive runtime. Unlike traditional linear notebooks, where code execution depends on the order of cells on the page, Grove treats each cell as a node in a dataflow graph.
Key Characteristics of the Grove DAG
- Dependency-Based Execution: Cells are executed based on their topological order. If Cell B references a variable defined in Cell A, a directed edge is created from A to B ((A \to B)). Cell B will only run after Cell A has successfully evaluated.
- Reactivity: When a value in a “parent” cell changes (e.g., via an input slider), the Grove runtime automatically propagates that change through the DAG, re-evaluating only the “downstream” cells.
- Acyclic Constraint: The graph must be “acyclic.” Circular dependencies (e.g., Cell A depends on B, which depends on A) are prohibited and will cause the Grove runtime to throw an error.
- Order Independence: Because the DAG determines execution, the physical position of cells in Grove does not matter. A variable defined at the bottom of the page can be used at the top; the runtime handles the sequence correctly.
Implicit Handling of Promises and Generators
The Grove DAG also manages asynchronous state. If a cell returns a Promise, downstream cells in the DAG implicitly wait for it to resolve. If a cell is a Generator, the DAG triggers a re-evaluation of all dependent cells every time a new value is yielded.
State Management Examples
Basic reactive variable
<!--{"pinCode":false,"dname":"reactive-variable-cell1","codeMode":"js","hide":false}-->
```js
x = {
return 1 + 1;
}
```
<!--{"pinCode":false,"dname":"reactive-variable-cell2","codeMode":"js","hide":false}-->
```js
y = {
return x + 2;
}
```
Reactive variable with a promise
<!--{"pinCode":false,"dname":"reactive-variable-cell3","codeMode":"js","hide":false}-->
```js
z = {
return await fetch("https://api.example.com/data");
}
```
<!--{"pinCode":false,"dname":"reactive-variable-cell4","codeMode":"js","hide":false}-->
```js
w = {
return (await z).json();
}
```
viewof
A cell which declares a viewof variable is a reactive variable.
<!--{"pinCode":false,"dname":"reactive-variable-cell5","codeMode":"js","hide":false}-->
```js
viewof x = Inputs.text({label: "Name", placeholder: "What's your name?"});
```
mutable variable
A â mutable cell allows you to break the standard functional flow of the DAG by creating a variable that can be updated via assignment from other cells. While normal cells are “read-only” to their neighbors, a mutable variable provides a controlled way to handle state that changes in response to events or specific logic.
<!--{"pinCode":false,"dname":"mutable-variable-cell1","codeMode":"js","hide":false}-->
```js
mutable counter = 0
```
<!--{"pinCode":false,"dname":"mutable-variable-cell2","codeMode":"js","hide":false}-->
```js
Button("Increment", async () => {
mutable counter = counter + 1;
});
```
Best Practices
- Prefer AntD components.
- Use the
graphvariable when creating cells which depend on the graph in the canvas.