diff --git a/packages/observablehq-compiler/index-kit.html b/packages/observablehq-compiler/index-kit.html
index 3236282709..be1e2fe9a7 100644
--- a/packages/observablehq-compiler/index-kit.html
+++ b/packages/observablehq-compiler/index-kit.html
@@ -5,13 +5,12 @@
Home
ObservableHQ Kit Preview
-
-
+
diff --git a/packages/observablehq-compiler/src/compiler.ts b/packages/observablehq-compiler/src/compiler.ts
index 785c6a4450..f4e432c375 100644
--- a/packages/observablehq-compiler/src/compiler.ts
+++ b/packages/observablehq-compiler/src/compiler.ts
@@ -1,5 +1,5 @@
-import { type Notebook, type Definition, compile as compileKit, fixRelativeUrl, isRelativePath, obfuscatedImport } from "./kit/index.ts";
+import { type Notebook, type Definition, compileNotebook, fixRelativeUrl, isRelativePath, obfuscatedImport } from "./kit/index.ts";
import { ohq, splitModule } from "./observable-shim.ts";
import { parseCell, ParsedImportCell } from "./cst.ts";
import { Writer } from "./writer.ts";
@@ -330,7 +330,7 @@ export async function compile(notebookOrOjs: ohq.Notebook, options?: CompileOpti
export async function compile(notebookOrOjs: string, options?: CompileOptions): Promise;
export async function compile(notebookOrOjs: Notebook | ohq.Notebook | string, { baseUrl = ".", importMode = "precompiled" }: CompileOptions = {}) {
if (isNotebookKit(notebookOrOjs)) {
- return compileKit(notebookOrOjs);
+ return compileNotebook(notebookOrOjs);
} else if (typeof notebookOrOjs === "string") {
notebookOrOjs = ojs2notebook(notebookOrOjs);
}
diff --git a/packages/observablehq-compiler/src/index.ts b/packages/observablehq-compiler/src/index.ts
index 6b00daf765..086d448a6e 100644
--- a/packages/observablehq-compiler/src/index.ts
+++ b/packages/observablehq-compiler/src/index.ts
@@ -2,5 +2,5 @@ export type { ohq } from "./observable-shim.ts";
export * from "./compiler.ts";
export { ojs2notebook, omd2notebook, omd2notebookKit, ojs2notebookKit, download } from "./util.ts";
-export { compile as compileKit, html2notebook, notebook2html } from "./kit/index.ts";
+export * from "./kit/index.ts";
export * from "./writer.ts";
diff --git a/packages/observablehq-compiler/src/kit/compiler.ts b/packages/observablehq-compiler/src/kit/compiler.ts
index c9479c9963..8a59402113 100644
--- a/packages/observablehq-compiler/src/kit/compiler.ts
+++ b/packages/observablehq-compiler/src/kit/compiler.ts
@@ -1,5 +1,5 @@
-import { type Notebook, transpile } from "@observablehq/notebook-kit";
+import { type Notebook, type Cell, transpile } from "@observablehq/notebook-kit";
import { type Definition } from "@observablehq/notebook-kit/runtime";
import { constructFunction } from "./util.ts";
@@ -7,35 +7,50 @@ export interface CompileKitOptions {
inline?: boolean;
}
-export function compile(notebook: Notebook, options: CompileKitOptions = { inline: true }): Definition[] {
+export function compileCell(cell: Cell, options: CompileKitOptions = { inline: true }): Definition[] {
const retVal: Definition[] = [];
- let id = 1;
- for (const cell of notebook.cells) {
- try {
- const compiled = transpile(cell);
- retVal.push({
- id: id++,
- ...compiled,
- body: options.inline ? constructFunction(compiled.body, `cell_${id}`) : compiled.body,
- });
- if (cell.pinned) {
- const compiled = transpile({
- ...cell,
- mode: "md",
- value: `\
+ const sourceIDOffset = 1000000;
+ try {
+ const compiled = transpile(cell);
+ retVal.push({
+ id: cell.id,
+ ...compiled,
+ body: options.inline ? constructFunction(compiled.body, `cell_${cell.id}`) : compiled.body,
+ });
+ if (cell.pinned) {
+ const compiled = transpile({
+ ...cell,
+ mode: "md",
+ value: `\
\`\`\`${cell.mode}
${cell.value}
\`\`\``
- });
- retVal.push({
- id: id++,
- ...compiled,
- body: options.inline ? constructFunction(compiled.body, `cell_${id}`) : compiled.body,
- });
- }
- } catch (error) {
- console.error(`Error compiling cell ${id}:`, error);
+ });
+ retVal.push({
+ id: sourceIDOffset + cell.id,
+ ...compiled,
+ body: options.inline ? constructFunction(compiled.body, `cell_source_${sourceIDOffset + cell.id}`) : compiled.body,
+ });
}
+ } catch (error) {
+ console.error(`Error compiling cell ${cell.id}:`, error);
+ }
+ return retVal;
+}
+
+export function compileNotebook(notebook: Notebook, options: CompileKitOptions = { inline: true }): Definition[] {
+ const retVal: Definition[] = [];
+ for (const cell of notebook.cells) {
+ const cellDefs = compileCell(cell, options);
+ retVal.push(...cellDefs);
}
return retVal;
}
+
+export function resetCellIDs(notebook: Notebook, start: number = 0, increment: number = 1): Notebook {
+ for (const cell of notebook.cells) {
+ cell.id = start;
+ start += increment;
+ }
+ return notebook;
+}
diff --git a/packages/observablehq-compiler/src/kit/index.ts b/packages/observablehq-compiler/src/kit/index.ts
index 6d5695c998..e88eec58a1 100644
--- a/packages/observablehq-compiler/src/kit/index.ts
+++ b/packages/observablehq-compiler/src/kit/index.ts
@@ -1,3 +1,2 @@
-export * from "./util.ts";
export * from "./compiler.ts";
export * from "./util.ts";
diff --git a/packages/observablehq-compiler/src/kit/runtime.ts b/packages/observablehq-compiler/src/kit/runtime.ts
index fae26f24a6..b96c48089a 100644
--- a/packages/observablehq-compiler/src/kit/runtime.ts
+++ b/packages/observablehq-compiler/src/kit/runtime.ts
@@ -4,6 +4,8 @@ import { type Definition } from "./index.ts";
import "@observablehq/notebook-kit/index.css";
import "@observablehq/notebook-kit/theme-air.css";
+export { DefineState };
+
export class NotebookRuntime extends NotebookRuntimeBase {
stateById = new Map();
@@ -16,23 +18,50 @@ export class NotebookRuntime extends NotebookRuntimeBase {
return this.stateById.has(cellId);
}
- async add(cellId: number, definition: Definition, placeholderDiv: HTMLDivElement): Promise {
- let state: DefineState | undefined = this.stateById.get(cellId);
- if (state) {
- this.remove(cellId);
+ async add(definition: Definition): Promise {
+ if (this.stateById.has(definition.id)) {
+ throw new Error(`Cell with id ${definition.id} already exists`);
}
- state = { root: placeholderDiv, expanded: [], variables: [] };
- this.stateById.set(cellId, state);
+ const placeholderDiv = document.createElement("div");
+ placeholderDiv.className = "observablehq observablehq--cell";
+ const state = { root: placeholderDiv, expanded: [], variables: [] };
+ this.stateById.set(definition.id, state);
this.define(state, definition);
await this.runtime._computeNow();
+ return state.root;
}
- async remove(cellId: number): Promise {
+ async update(definition: Definition): Promise {
+ const state = this.stateById.get(definition.id);
+ if (!state) {
+ throw new Error(`Cell with id ${definition.id} does not exist`);
+ }
+ await this.clear(definition.id);
+ this.define(state, definition);
+ await this.runtime._computeNow();
+ return state.root;
+ }
+
+ async clear(cellId: number): Promise {
const state = this.stateById.get(cellId);
- if (!state) return;
+ if (!state) {
+ throw new Error(`Cell with id ${cellId} does not exist`);
+ }
[...state.variables].forEach(v => v.delete());
+ await this.runtime._computeNow();
+ state.variables = [];
+ state.expanded = [];
+ state.root.innerHTML = "";
+ }
+
+ async remove(cellId: number): Promise {
+ const state = this.stateById.get(cellId);
+ if (!state) {
+ throw new Error(`Cell with id ${cellId} does not exist`);
+ }
+ void this.clear(cellId);
this.stateById.delete(cellId);
- state.root?.remove();
+ state.root.remove();
await this.runtime._computeNow();
}
@@ -44,14 +73,15 @@ export class NotebookRuntime extends NotebookRuntimeBase {
await this.runtime._computeNow();
}
- render(definitions: Definition[], target: HTMLElement) {
- definitions.forEach(definition => {
- const placeholderDiv = document.createElement("div");
- placeholderDiv.id = `cell-${definition.id}`;
- placeholderDiv.className = "observablehq observablehq--cell";
- this.stateById.set(definition.id, { root: placeholderDiv, expanded: [], variables: [] });
- this.define(this.stateById.get(definition.id)!, definition);
- target.appendChild(placeholderDiv);
- });
+ async render(definitions: Definition[], target: HTMLElement) {
+ for (const definition of definitions) {
+ let observableDiv: HTMLDivElement;
+ if (this.stateById.has(definition.id)) {
+ observableDiv = await this.update(definition);
+ } else {
+ observableDiv = await this.add(definition);
+ }
+ target.appendChild(observableDiv);
+ }
}
}
diff --git a/packages/observablehq-compiler/tests/index-notebookkit.js b/packages/observablehq-compiler/tests/index-notebookkit.js
index e1e88fcfd4..85e6e3c85c 100644
--- a/packages/observablehq-compiler/tests/index-notebookkit.js
+++ b/packages/observablehq-compiler/tests/index-notebookkit.js
@@ -1,4 +1,4 @@
-import { compileKit, html2notebook } from "@hpcc-js/observablehq-compiler";
+import { compileNotebook, html2notebook } from "@hpcc-js/observablehq-compiler";
import { NotebookRuntime } from "@hpcc-js/observablehq-compiler/runtime";
// import "@observablehq/notebook-kit/theme-air.css";
@@ -19,13 +19,13 @@ import { NotebookRuntime } from "@hpcc-js/observablehq-compiler/runtime";
export async function testHtml(target) {
- const element = document.getElementById(target);
- if (!element) {
- throw new Error(`Element with id ${target} not found`);
- }
+ const element = document.getElementById(target);
+ if (!element) {
+ throw new Error(`Element with id ${target} not found`);
+ }
- // const html = await fetch("../tests/albers-usa-projection.txt");
- const html = `\
+ // const html = await fetch("../tests/albers-usa-projection.txt");
+ const html = `\
Observable Notebooks 2.0 System guide
@@ -210,8 +210,8 @@ export async function testHtml(target) {
`;
- const notebook = html2notebook(html);
- const definitions = compileKit(notebook);
- const runtime = new NotebookRuntime();
- runtime.render(definitions, element);
+ const notebook = html2notebook(html);
+ const definitions = compileNotebook(notebook);
+ const runtime = new NotebookRuntime();
+ runtime.render(definitions, element);
}
diff --git a/packages/observablehq-compiler/tests/index-notebookkit.ts b/packages/observablehq-compiler/tests/index-notebookkit.ts
index 3c90299e86..4efb8b6292 100644
--- a/packages/observablehq-compiler/tests/index-notebookkit.ts
+++ b/packages/observablehq-compiler/tests/index-notebookkit.ts
@@ -1,4 +1,4 @@
-import { omd2notebookKit, ojs2notebookKit, compileKit, html2notebook } from "../src/index.ts";
+import { omd2notebookKit, ojs2notebookKit, compileNotebook, html2notebook } from "../src/index.ts";
import { NotebookRuntime } from "../src/kit/runtime.ts";
// import "@observablehq/notebook-kit/theme-air.css";
@@ -6,7 +6,7 @@ import { NotebookRuntime } from "../src/kit/runtime.ts";
// import "@observablehq/notebook-kit/theme-cotton.css";
// import "@observablehq/notebook-kit/theme-deep-space.css";
// import "@observablehq/notebook-kit/theme-glacier.css";
-// import "@observablehq/notebook-kit/theme-ink.css";
+import "@observablehq/notebook-kit/theme-ink.css";
// import "@observablehq/notebook-kit/theme-midnight.css";
// import "@observablehq/notebook-kit/theme-near-midnight.css";
// import "@observablehq/notebook-kit/theme-ocean-floor.css";
@@ -19,28 +19,28 @@ import { NotebookRuntime } from "../src/kit/runtime.ts";
export async function testHtml(target: string): Promise {
- const element = document.getElementById(target) as HTMLDivElement;
- if (!element) {
- throw new Error(`Element with id ${target} not found`);
- }
-
- // const html = await fetch("../tests/albers-usa-projection.txt");
- const html = await fetch("../tests/system-guide.txt")
- .then((response) => {
- return response.text();
- });
- const notebook = html2notebook(html);
- const definitions = compileKit(notebook);
- const runtime = new NotebookRuntime();
- runtime.render(definitions, element);
+ const element = document.getElementById(target) as HTMLDivElement;
+ if (!element) {
+ throw new Error(`Element with id ${target} not found`);
+ }
+
+ // const html = await fetch("../tests/albers-usa-projection.txt");
+ const html = await fetch("../tests/system-guide.txt")
+ .then((response) => {
+ return response.text();
+ });
+ const notebook = html2notebook(html);
+ const definitions = compileNotebook(notebook);
+ const runtime = new NotebookRuntime();
+ runtime.render(definitions, element);
}
export function testOmd(target: string): void {
- const element = document.getElementById(target) as HTMLDivElement;
- if (!element) {
- throw new Error(`Element with id ${target} not found`);
- }
- const omd2 = `\
+ const element = document.getElementById(target) as HTMLDivElement;
+ if (!element) {
+ throw new Error(`Element with id ${target} not found`);
+ }
+ const omd2 = `\
## Imports (dot)
@@ -331,18 +331,18 @@ value
`;
- const notebook = omd2notebookKit(omd2);
- const definitions = compileKit(notebook);
- const runtime = new NotebookRuntime();
- runtime.render(definitions, element);
+ const notebook = omd2notebookKit(omd2);
+ const definitions = compileKit(notebook);
+ const runtime = new NotebookRuntime();
+ runtime.render(definitions, element);
}
export function testOjs(target: string): void {
- const element = document.getElementById(target) as HTMLDivElement;
- if (!element) {
- throw new Error(`Element with id ${target} not found`);
- }
- const ojs = `\
+ const element = document.getElementById(target) as HTMLDivElement;
+ if (!element) {
+ throw new Error(`Element with id ${target} not found`);
+ }
+ const ojs = `\
2 * 3 * 7
{
let sum = 0;
@@ -357,8 +357,8 @@ md \`# lib.ojs\`;
mo = 38 + 40;
`;
- const notebook = ojs2notebookKit(ojs);
- const definitions = compileKit(notebook);
- const runtime = new NotebookRuntime();
- runtime.render(definitions, element);
+ const notebook = ojs2notebookKit(ojs);
+ const definitions = compileKit(notebook);
+ const runtime = new NotebookRuntime();
+ runtime.render(definitions, element);
}
\ No newline at end of file