-
Notifications
You must be signed in to change notification settings - Fork 64
fix: improve runtime placeholder handling #4447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,41 +1,56 @@ | ||
|
|
||
| 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"; | ||
|
|
||
| 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; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,2 @@ | ||
| export * from "./util.ts"; | ||
| export * from "./compiler.ts"; | ||
| export * from "./util.ts"; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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<number, DefineState>(); | ||||||
|
|
@@ -16,23 +18,50 @@ export class NotebookRuntime extends NotebookRuntimeBase { | |||||
| return this.stateById.has(cellId); | ||||||
| } | ||||||
|
|
||||||
| async add(cellId: number, definition: Definition, placeholderDiv: HTMLDivElement): Promise<void> { | ||||||
| let state: DefineState | undefined = this.stateById.get(cellId); | ||||||
| if (state) { | ||||||
| this.remove(cellId); | ||||||
| async add(definition: Definition): Promise<HTMLDivElement> { | ||||||
| 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<void> { | ||||||
| async update(definition: Definition): Promise<HTMLDivElement> { | ||||||
| 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<void> { | ||||||
| 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<void> { | ||||||
| const state = this.stateById.get(cellId); | ||||||
| if (!state) { | ||||||
| throw new Error(`Cell with id ${cellId} does not exist`); | ||||||
| } | ||||||
| void this.clear(cellId); | ||||||
|
||||||
| void this.clear(cellId); | |
| await this.clear(cellId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The void is documenting that await is not needed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The magic number 1000000 should be defined as a named constant to improve code readability and maintainability. Consider defining it as
const SOURCE_ID_OFFSET = 1000000;at the top of the file.