@zikojs/notebook 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -4
- package/src/codeEditor.js +3 -3
- package/src/components.js +19 -17
- package/src/notebook.js +11 -8
- package/src/transform.js +12 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zikojs/notebook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -45,16 +45,14 @@
|
|
|
45
45
|
"@codemirror/view": "^6.43.11",
|
|
46
46
|
"@zikojs/lucide": "^0.2.2",
|
|
47
47
|
"acorn": "^8.18.0",
|
|
48
|
-
"canvas-confetti": "^1.9.4",
|
|
49
48
|
"codemirror": "^6.0.2",
|
|
50
49
|
"lucide": "^1.44.0",
|
|
51
|
-
"marked": "^18.0.12",
|
|
52
50
|
"rehype-stringify": "^10.0.1",
|
|
53
51
|
"remark": "^15.0.1",
|
|
54
52
|
"remark-rehype": "^11.1.2",
|
|
55
53
|
"vanjs-core": "^1.6.1",
|
|
56
54
|
"zextra": "^1.15.0",
|
|
57
|
-
"ziko": "^2.0.0-beta.
|
|
55
|
+
"ziko": "^2.0.0-beta.16.0"
|
|
58
56
|
},
|
|
59
57
|
"scripts": {
|
|
60
58
|
"test": "echo \"Error: no test specified\" && exit 1",
|
package/src/codeEditor.js
CHANGED
|
@@ -5,15 +5,15 @@ import { bracketMatching, indentUnit, syntaxHighlighting, defaultHighlightStyle
|
|
|
5
5
|
import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
6
6
|
import { javascript } from "@codemirror/lang-javascript";
|
|
7
7
|
import { markdown } from "@codemirror/lang-markdown";
|
|
8
|
-
import
|
|
8
|
+
import { tags } from "ziko/dom";
|
|
9
9
|
|
|
10
|
-
const { div } =
|
|
10
|
+
const { div } = tags;
|
|
11
11
|
|
|
12
12
|
const languageFor = (type) =>
|
|
13
13
|
type === "markdown" ? markdown() : javascript();
|
|
14
14
|
|
|
15
15
|
export function createCodeEditor(app, cellData, actions) {
|
|
16
|
-
const editorDom = div({ class: "input-wrapper" });
|
|
16
|
+
const editorDom = div({ class: "input-wrapper" }).element;
|
|
17
17
|
|
|
18
18
|
const languageCompartment = new Compartment();
|
|
19
19
|
const readOnlyCompartment = new Compartment();
|
package/src/components.js
CHANGED
|
@@ -21,16 +21,17 @@ import {
|
|
|
21
21
|
Loader2
|
|
22
22
|
} from './icons.js';
|
|
23
23
|
|
|
24
|
-
const {
|
|
24
|
+
const { button } = van.tags;
|
|
25
25
|
|
|
26
26
|
import { tags } from 'ziko/dom'
|
|
27
27
|
|
|
28
|
+
const { div, span } = tags
|
|
29
|
+
|
|
28
30
|
export const Button = ({ class: className = "", title = "", disabled = false, onclick }, ...children) =>
|
|
29
31
|
button({ class: `btn ${className}`, title, disabled, onclick }, ...children);
|
|
30
32
|
|
|
31
33
|
export function Header({ store, actions }) {
|
|
32
|
-
|
|
33
|
-
const statusBadge = span({ class: "status-badge" }, Loader2(), span('kkk'));
|
|
34
|
+
const statusBadge = span({ class: "status-badge" }, Loader2(), span('Loading ...')).element;
|
|
34
35
|
const runAllBtn = Button({ class: "btn-primary", onclick: actions.runAll }, PlayForward(), "Run All");
|
|
35
36
|
const clearBtn = Button({ onclick: actions.clearOutputs }, Eraser(), "Clear Outputs");
|
|
36
37
|
const addCodeBtn = Button({ onclick: actions.addCode }, Play(), "Add Code");
|
|
@@ -52,11 +53,11 @@ export function Header({ store, actions }) {
|
|
|
52
53
|
div({ class: "toolbar" },
|
|
53
54
|
runAllBtn, clearBtn, addCodeBtn, addMdBtn, deleteActiveBtn, exportBtn
|
|
54
55
|
)
|
|
55
|
-
);
|
|
56
|
+
).element;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
export function MarkdownView({ cellData, actions }) {
|
|
59
|
-
const content =
|
|
60
|
+
const content = div().element;
|
|
60
61
|
|
|
61
62
|
// Process Markdown asynchronously using Remark + Rehype
|
|
62
63
|
remark()
|
|
@@ -71,12 +72,12 @@ export function MarkdownView({ cellData, actions }) {
|
|
|
71
72
|
});
|
|
72
73
|
|
|
73
74
|
const wrap = div({ class: "markdown-rendered-cell markdown-body" }, content);
|
|
74
|
-
wrap.
|
|
75
|
-
return wrap;
|
|
75
|
+
wrap.onClick(() => actions.editMarkdown())
|
|
76
|
+
return wrap.element;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
export function CellControls({ cellData, store, actions }) {
|
|
79
|
-
const controlsContainer = div({ class: "cell-controls" });
|
|
80
|
+
const controlsContainer = div({ class: "cell-controls" }).element;
|
|
80
81
|
|
|
81
82
|
const renderButtons = () => {
|
|
82
83
|
const isQueueRunning = store.get('executionQueueState').isRunning;
|
|
@@ -149,7 +150,7 @@ export function CellControls({ cellData, store, actions }) {
|
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
export function CellItem({ cellData, app, store }) {
|
|
152
|
-
const outputRef = div({ class: "output-area" });
|
|
153
|
+
const outputRef = div({ class: "output-area" }).element;
|
|
153
154
|
if (cellData.outputNode) outputRef.appendChild(cellData.outputNode);
|
|
154
155
|
cellData._outputRef = outputRef;
|
|
155
156
|
|
|
@@ -157,22 +158,23 @@ export function CellItem({ cellData, app, store }) {
|
|
|
157
158
|
cellData._actions = actions;
|
|
158
159
|
cellData._editor = createCodeEditor(app, cellData, actions);
|
|
159
160
|
|
|
160
|
-
const inPrompt = span({ class: "prompt" });
|
|
161
|
-
const outPrompt = span({ class: "prompt out" });
|
|
162
|
-
const bodySlot = div({
|
|
161
|
+
const inPrompt = span({ class: "prompt" }).element;
|
|
162
|
+
const outPrompt = span({ class: "prompt out" }).element;
|
|
163
|
+
const bodySlot = div().style({ display : 'contents'}).element;
|
|
163
164
|
const controlsEl = CellControls({ cellData, store, actions });
|
|
164
165
|
|
|
165
|
-
const outputGrid = div({ class: "cell-output-grid" }, outPrompt, div({ class: "output-wrapper" }, outputRef));
|
|
166
|
-
const inputGrid = div({ class: "cell-input-grid" }, inPrompt, bodySlot, controlsEl);
|
|
166
|
+
const outputGrid = div({ class: "cell-output-grid" }, outPrompt, div({ class: "output-wrapper" }, outputRef)).element;
|
|
167
|
+
const inputGrid = div({ class: "cell-input-grid" }, inPrompt, bodySlot, controlsEl).element;
|
|
167
168
|
const root = div({ class: "cell" }, inputGrid, outputGrid);
|
|
168
|
-
root.
|
|
169
|
+
root.onClick(() => app.setActiveCell(cellData.id));
|
|
170
|
+
// root.onclick = () => app.setActiveCell(cellData.id);
|
|
169
171
|
|
|
170
|
-
cellData._dom = root;
|
|
172
|
+
cellData._dom = root.element;
|
|
171
173
|
cellData._inPrompt = inPrompt;
|
|
172
174
|
cellData._outPrompt = outPrompt;
|
|
173
175
|
cellData._bodySlot = bodySlot;
|
|
174
176
|
cellData._outputGrid = outputGrid;
|
|
175
177
|
|
|
176
178
|
app._refreshCell(cellData);
|
|
177
|
-
return root;
|
|
179
|
+
return root.element;
|
|
178
180
|
}
|
package/src/notebook.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import van from "vanjs-core";
|
|
2
1
|
import { NotebookStore } from "./store.js";
|
|
3
2
|
import { Header, CellItem, MarkdownView } from "./components.js";
|
|
4
3
|
import { evaluateCodeAsync } from "./transform.js";
|
|
4
|
+
import { tags, UIElement } from "ziko/dom";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export class UINotebook {
|
|
6
|
+
export class UINotebook extends UIElement{
|
|
9
7
|
constructor({
|
|
10
8
|
cells: initialCells = [],
|
|
11
9
|
importMap = {},
|
|
@@ -15,6 +13,7 @@ export class UINotebook {
|
|
|
15
13
|
minLines = 3,
|
|
16
14
|
maxCells = Infinity
|
|
17
15
|
} = {}) {
|
|
16
|
+
super({ element : 'div'})
|
|
18
17
|
this.id = crypto.randomUUID ? crypto.randomUUID() : 'nb_' + Math.random().toString(36).substring(2, 9);
|
|
19
18
|
|
|
20
19
|
if (!window.__notebook_scope_map) {
|
|
@@ -59,7 +58,7 @@ export class UINotebook {
|
|
|
59
58
|
addCodeBelow: () => this.addCell("code", "", this.store.get('activeCellId'))
|
|
60
59
|
};
|
|
61
60
|
|
|
62
|
-
this.cellsContainer = div({ class: "cells-container" });
|
|
61
|
+
this.cellsContainer = tags.div({ class: "cells-container" }).element;
|
|
63
62
|
this.header = Header({
|
|
64
63
|
store: this.store,
|
|
65
64
|
actions: {
|
|
@@ -72,7 +71,11 @@ export class UINotebook {
|
|
|
72
71
|
}
|
|
73
72
|
});
|
|
74
73
|
|
|
75
|
-
this.
|
|
74
|
+
this.append(
|
|
75
|
+
this.header,
|
|
76
|
+
tags.div({ class: "notebook" },
|
|
77
|
+
this.cellsContainer)
|
|
78
|
+
)
|
|
76
79
|
|
|
77
80
|
this._renderAllCells();
|
|
78
81
|
|
|
@@ -117,7 +120,7 @@ export class UINotebook {
|
|
|
117
120
|
await evaluateCodeAsync(code, cellData._outputRef, app.importMap, scope);
|
|
118
121
|
} catch (err) {
|
|
119
122
|
hasError = true;
|
|
120
|
-
|
|
123
|
+
tags.div({ class: "error-output" }, err.toString()).mount(cellData._outputRef)
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
const counter = this.store.get('executionCounter');
|
|
@@ -125,7 +128,7 @@ export class UINotebook {
|
|
|
125
128
|
cellData.execCount = counter;
|
|
126
129
|
cellData.hasDomOutput = cellData._outputRef.childNodes.length > 0 || hasError;
|
|
127
130
|
|
|
128
|
-
const container = div({ class: "output-area" });
|
|
131
|
+
const container = tags.div({ class: "output-area" }).element;
|
|
129
132
|
while (cellData._outputRef.firstChild) container.appendChild(cellData._outputRef.firstChild);
|
|
130
133
|
cellData.outputNode = container;
|
|
131
134
|
cellData._outputRef.appendChild(container);
|
package/src/transform.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import van from "vanjs-core";
|
|
2
1
|
import * as acorn from "acorn";
|
|
3
2
|
|
|
4
3
|
export const rewriteImportNode = (node, code, importMapConfig) => {
|
|
@@ -21,19 +20,19 @@ export const rewriteImportNode = (node, code, importMapConfig) => {
|
|
|
21
20
|
else if (spec.type === "ImportSpecifier") namedImports.push({ imported: spec.imported.name, local: spec.local.name });
|
|
22
21
|
});
|
|
23
22
|
|
|
24
|
-
if (namespaceId) return `
|
|
23
|
+
if (namespaceId) return `__scope__.${namespaceId} = await import("${source}");`;
|
|
25
24
|
|
|
26
25
|
const tmp = `__mod_${node.start}`;
|
|
27
26
|
let lines = [`const ${tmp} = await import("${source}");`];
|
|
28
27
|
|
|
29
28
|
if (defaultImportName && namedImports.length === 0) {
|
|
30
|
-
lines.push(`
|
|
29
|
+
lines.push(`__scope__.${defaultImportName} = ${tmp}.default ?? ${tmp};`);
|
|
31
30
|
return lines.join("\n");
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
if (defaultImportName) lines.push(`
|
|
33
|
+
if (defaultImportName) lines.push(`__scope__.${defaultImportName} = ${tmp}.default ?? ${tmp};`);
|
|
35
34
|
namedImports.forEach(({ imported, local }) => {
|
|
36
|
-
lines.push(`
|
|
35
|
+
lines.push(`__scope__.${local} = ${tmp}.${imported} !== undefined ? ${tmp}.${imported} : (${tmp}.default ?? ${tmp});`);
|
|
37
36
|
});
|
|
38
37
|
|
|
39
38
|
return lines.join("\n");
|
|
@@ -53,7 +52,7 @@ export const transformImportsAndScope = (code, importMapConfig) => {
|
|
|
53
52
|
if (decl.id.type === "Identifier") {
|
|
54
53
|
const varName = decl.id.name;
|
|
55
54
|
const initCode = decl.init ? code.slice(decl.init.start, decl.init.end) : "undefined";
|
|
56
|
-
replacementCode += `
|
|
55
|
+
replacementCode += `__scope__.${varName} = ${initCode};\n`;
|
|
57
56
|
} else if (decl.id.type === "ObjectPattern") {
|
|
58
57
|
const initCode = decl.init ? code.slice(decl.init.start, decl.init.end) : "undefined";
|
|
59
58
|
const tempVar = `__destruct_obj_${decl.start}`;
|
|
@@ -62,7 +61,7 @@ export const transformImportsAndScope = (code, importMapConfig) => {
|
|
|
62
61
|
if (prop.value && prop.value.type === "Identifier") {
|
|
63
62
|
const localName = prop.value.name;
|
|
64
63
|
const keyName = prop.key.type === "Identifier" ? prop.key.name : prop.key.value;
|
|
65
|
-
replacementCode += `
|
|
64
|
+
replacementCode += `__scope__.${localName} = ${tempVar}.${keyName}; `;
|
|
66
65
|
}
|
|
67
66
|
});
|
|
68
67
|
replacementCode += `})();\n`;
|
|
@@ -73,7 +72,7 @@ export const transformImportsAndScope = (code, importMapConfig) => {
|
|
|
73
72
|
decl.id.elements.forEach((elem, index) => {
|
|
74
73
|
if (elem && elem.type === "Identifier") {
|
|
75
74
|
const localName = elem.name;
|
|
76
|
-
replacementCode += `
|
|
75
|
+
replacementCode += `__scope__.${localName} = ${tempVar}[${index}]; `;
|
|
77
76
|
}
|
|
78
77
|
});
|
|
79
78
|
replacementCode += `})();\n`;
|
|
@@ -83,11 +82,11 @@ export const transformImportsAndScope = (code, importMapConfig) => {
|
|
|
83
82
|
} else if (node.type === "FunctionDeclaration" && node.id) {
|
|
84
83
|
const funcName = node.id.name;
|
|
85
84
|
const funcBody = code.slice(node.start, node.end);
|
|
86
|
-
modifications.push({ start: node.start, end: node.end, replacement: `
|
|
85
|
+
modifications.push({ start: node.start, end: node.end, replacement: `__scope__.${funcName} = ${funcBody.replace(/^function\s+\w+/, "function")}` });
|
|
87
86
|
} else if (node.type === "ClassDeclaration" && node.id) {
|
|
88
87
|
const className = node.id.name;
|
|
89
88
|
const classBody = code.slice(node.start, node.end);
|
|
90
|
-
modifications.push({ start: node.start, end: node.end, replacement: `
|
|
89
|
+
modifications.push({ start: node.start, end: node.end, replacement: `__scope__.${className} = ${classBody.replace(/^class\s+\w+/, "class")}` });
|
|
91
90
|
}
|
|
92
91
|
});
|
|
93
92
|
|
|
@@ -105,16 +104,13 @@ export const evaluateCodeAsync = async (code, TARGET, importMapConfig, scope) =>
|
|
|
105
104
|
const validScope = scope || Object.create(null);
|
|
106
105
|
const compiledCode = transformImportsAndScope(code, importMapConfig);
|
|
107
106
|
|
|
108
|
-
const runCell = new Function("TARGET", "
|
|
107
|
+
const runCell = new Function("TARGET", "__scope__", `
|
|
109
108
|
return (async () => {
|
|
110
|
-
with (
|
|
109
|
+
with (__scope__) {
|
|
111
110
|
${compiledCode}
|
|
112
111
|
}
|
|
113
112
|
})();
|
|
114
113
|
`);
|
|
115
114
|
|
|
116
|
-
const result = await runCell(TARGET,
|
|
117
|
-
if (result !== undefined && result !== null) {
|
|
118
|
-
van.add(TARGET, result);
|
|
119
|
-
}
|
|
115
|
+
const result = await runCell(TARGET, validScope);
|
|
120
116
|
};
|