@zikojs/notebook 0.14.2 → 0.15.1
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/LICENSE +21 -21
- package/package.json +3 -4
- package/src/code-editor/index.js +302 -249
- package/src/compiler/evaluate.js +129 -120
- package/src/compiler/imports.js +189 -126
- package/src/compiler/index.js +11 -11
- package/src/compiler/scope.js +94 -94
- package/src/compiler/transform.js +115 -111
- package/src/components/Button/index.css +54 -54
- package/src/components/Button/index.js +4 -4
- package/src/components/CellItem/index.js +180 -180
- package/src/components/ErrorOutput/index.css +9 -9
- package/src/components/ErrorOutput/index.js +1 -1
- package/src/components/Header/index.css +46 -46
- package/src/components/Header/index.js +47 -47
- package/src/components/MarkdownView/index.css +91 -91
- package/src/components/MarkdownView/index.js +134 -132
- package/src/components/icons/index.js +31 -31
- package/src/components/index.css +22 -22
- package/src/components/index.js +3 -3
- package/src/index.css +176 -172
- package/src/index.js +1 -1
- package/src/index.ts +96 -96
- package/src/notebook.js +816 -792
- package/src/store.js +37 -37
|
@@ -1,180 +1,180 @@
|
|
|
1
|
-
import { createCodeEditor } from "../../code-editor/index.js";
|
|
2
|
-
import { tags } from 'ziko/dom'
|
|
3
|
-
import { useState } from 'ziko/hooks'
|
|
4
|
-
import {
|
|
5
|
-
Check,
|
|
6
|
-
Copy,
|
|
7
|
-
Lock,
|
|
8
|
-
Unlock,
|
|
9
|
-
Code2,
|
|
10
|
-
FileText,
|
|
11
|
-
Pencil,
|
|
12
|
-
Play,
|
|
13
|
-
Refresh,
|
|
14
|
-
Plus,
|
|
15
|
-
Trash2,
|
|
16
|
-
} from '../icons/index.js';
|
|
17
|
-
const { div, span } = tags;
|
|
18
|
-
|
|
19
|
-
import { Button } from "../Button/index.js";
|
|
20
|
-
|
|
21
|
-
export const Prompt = () => {
|
|
22
|
-
return span({ class: "prompt" })
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function CellItem({ cellData, app, store }) {
|
|
26
|
-
const outputRef = div({ class: "output-area" });
|
|
27
|
-
if (cellData.outputNode) outputRef.append(cellData.outputNode);
|
|
28
|
-
cellData._outputRef = outputRef.element;
|
|
29
|
-
|
|
30
|
-
const actions = app._cellActions(cellData);
|
|
31
|
-
cellData._actions = actions;
|
|
32
|
-
cellData._editor = createCodeEditor(app, cellData, actions);
|
|
33
|
-
|
|
34
|
-
/* Check ziko */
|
|
35
|
-
const [In, setIn] = useState(0);
|
|
36
|
-
globalThis.setIn = setIn;
|
|
37
|
-
const inPrompt = span({ class: "prompt" }, In);
|
|
38
|
-
const outPrompt = span({ class: "prompt out" });
|
|
39
|
-
const bodySlot = div().style({ display: 'contents' });
|
|
40
|
-
|
|
41
|
-
// Pass runtimePlugins from app into CellControls
|
|
42
|
-
const controlsEl = CellControls({
|
|
43
|
-
cellData,
|
|
44
|
-
store,
|
|
45
|
-
actions,
|
|
46
|
-
runtimePlugins: app.runtimePlugins
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const inputRow = div({ class: "cell-input-row" }, inPrompt, bodySlot);
|
|
50
|
-
const outputRow = div({ class: "cell-output-row" }, outPrompt, div({ class: "output-wrapper" }, outputRef));
|
|
51
|
-
const root = div({ class: "cell" }, inputRow, controlsEl, outputRow);
|
|
52
|
-
root.onClick(() => app.setActiveCell(cellData.id));
|
|
53
|
-
|
|
54
|
-
cellData._dom = root.element;
|
|
55
|
-
cellData._inPrompt = inPrompt.element;
|
|
56
|
-
cellData._outPrompt = outPrompt.element;
|
|
57
|
-
cellData._bodySlot = bodySlot.element;
|
|
58
|
-
cellData._outputRow = outputRow.element;
|
|
59
|
-
cellData._setIn = setIn;
|
|
60
|
-
|
|
61
|
-
app._refreshCell(cellData);
|
|
62
|
-
return root.element;
|
|
63
|
-
}
|
|
64
|
-
export function CellControls({ cellData, store, actions, runtimePlugins = [] }) {
|
|
65
|
-
const controlsContainer = div({ class: "cell-controls" }).element;
|
|
66
|
-
|
|
67
|
-
const renderButtons = () => {
|
|
68
|
-
const isQueueRunning = store.get('executionQueueState').isRunning;
|
|
69
|
-
const isMaxCellsReached = store.get('cells').length >= store.get('maxCells');
|
|
70
|
-
const isCopied = cellData._copiedFlag === true;
|
|
71
|
-
|
|
72
|
-
// Build the runtime selector dropdown for code cells using native DOM options
|
|
73
|
-
let runtimeSelectEl = null;
|
|
74
|
-
if (cellData.type === "code") {
|
|
75
|
-
const selectElement = document.createElement("select");
|
|
76
|
-
selectElement.className = "runtime-select";
|
|
77
|
-
selectElement.title = "Select Cell Runtime";
|
|
78
|
-
selectElement.disabled = cellData.readonly;
|
|
79
|
-
|
|
80
|
-
selectElement.onchange = (e) => {
|
|
81
|
-
actions.setRuntime(e.target.value === "" ? null : e.target.value);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// Default JS option
|
|
85
|
-
const optJs = document.createElement("option");
|
|
86
|
-
optJs.value = "";
|
|
87
|
-
optJs.textContent = "JS";
|
|
88
|
-
selectElement.appendChild(optJs);
|
|
89
|
-
|
|
90
|
-
// Runtime plugin options
|
|
91
|
-
for (const p of runtimePlugins) {
|
|
92
|
-
const opt = document.createElement("option");
|
|
93
|
-
opt.value = p.name;
|
|
94
|
-
opt.textContent = p.name.charAt(0).toUpperCase() + p.name.slice(1);
|
|
95
|
-
selectElement.appendChild(opt);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Explicitly set the active value
|
|
99
|
-
selectElement.value = cellData.runtime || "";
|
|
100
|
-
runtimeSelectEl = selectElement;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const children = [
|
|
104
|
-
Button({
|
|
105
|
-
class: `btn-icon ${isCopied ? "btn-active" : ""}`,
|
|
106
|
-
title: isCopied ? "Copied!" : "Copy Cell Content",
|
|
107
|
-
onclick: async () => {
|
|
108
|
-
await actions.copyCode();
|
|
109
|
-
cellData._copiedFlag = true;
|
|
110
|
-
renderButtons();
|
|
111
|
-
setTimeout(() => {
|
|
112
|
-
cellData._copiedFlag = false;
|
|
113
|
-
renderButtons();
|
|
114
|
-
}, 1500);
|
|
115
|
-
}
|
|
116
|
-
}, isCopied ? Check() : Copy()),
|
|
117
|
-
|
|
118
|
-
Button({
|
|
119
|
-
class: `btn-icon ${cellData.readonly ? "btn-active" : ""}`,
|
|
120
|
-
title: cellData.readonly ? "Make Editable" : "Make Read-Only",
|
|
121
|
-
disabled: isQueueRunning,
|
|
122
|
-
onclick: actions.toggleReadonly,
|
|
123
|
-
}, cellData.readonly ? Lock() : Unlock()),
|
|
124
|
-
|
|
125
|
-
Button({
|
|
126
|
-
class: `btn-icon ${cellData.type === "markdown" ? "btn-active" : ""}`,
|
|
127
|
-
title: `Switch to ${cellData.type === "code" ? "Markdown" : "JS Code"}`,
|
|
128
|
-
disabled: isQueueRunning || cellData.readonly,
|
|
129
|
-
onclick: actions.toggleType,
|
|
130
|
-
}, cellData.type === "code" ? FileText() : Code2()),
|
|
131
|
-
|
|
132
|
-
(cellData.type === "markdown" && !cellData.isEditingMarkdown)
|
|
133
|
-
? Button({
|
|
134
|
-
class: "btn-icon",
|
|
135
|
-
title: "Edit Markdown",
|
|
136
|
-
disabled: isQueueRunning || cellData.readonly,
|
|
137
|
-
onclick: actions.editMarkdown
|
|
138
|
-
}, Pencil())
|
|
139
|
-
: div(
|
|
140
|
-
Button({
|
|
141
|
-
class: "btn-icon",
|
|
142
|
-
title: "Run (Shift+Enter)",
|
|
143
|
-
disabled: isQueueRunning,
|
|
144
|
-
onclick: () => actions.runCode(false)
|
|
145
|
-
}, Refresh()),
|
|
146
|
-
Button({
|
|
147
|
-
class: "btn-icon btn-primary",
|
|
148
|
-
title: "Run & Advance (Shift+Enter)",
|
|
149
|
-
disabled: isQueueRunning,
|
|
150
|
-
onclick: () => actions.runCode(true)
|
|
151
|
-
}, Play())
|
|
152
|
-
).style({ display : 'contents'}).element,
|
|
153
|
-
|
|
154
|
-
Button({
|
|
155
|
-
class: "btn-icon",
|
|
156
|
-
title: "Add Cell Below",
|
|
157
|
-
disabled: isQueueRunning || isMaxCellsReached,
|
|
158
|
-
onclick: actions.addBelow
|
|
159
|
-
}, Plus()),
|
|
160
|
-
|
|
161
|
-
Button({
|
|
162
|
-
class: "btn-icon",
|
|
163
|
-
title: "Delete Cell",
|
|
164
|
-
disabled: isQueueRunning,
|
|
165
|
-
onclick: actions.deleteCell
|
|
166
|
-
}, Trash2())
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
if (runtimeSelectEl) {
|
|
170
|
-
children.unshift(runtimeSelectEl);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
controlsContainer.replaceChildren(...children);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
renderButtons();
|
|
177
|
-
store.on('change', renderButtons);
|
|
178
|
-
|
|
179
|
-
return controlsContainer;
|
|
180
|
-
}
|
|
1
|
+
import { createCodeEditor } from "../../code-editor/index.js";
|
|
2
|
+
import { tags } from 'ziko/dom'
|
|
3
|
+
import { useState } from 'ziko/hooks'
|
|
4
|
+
import {
|
|
5
|
+
Check,
|
|
6
|
+
Copy,
|
|
7
|
+
Lock,
|
|
8
|
+
Unlock,
|
|
9
|
+
Code2,
|
|
10
|
+
FileText,
|
|
11
|
+
Pencil,
|
|
12
|
+
Play,
|
|
13
|
+
Refresh,
|
|
14
|
+
Plus,
|
|
15
|
+
Trash2,
|
|
16
|
+
} from '../icons/index.js';
|
|
17
|
+
const { div, span } = tags;
|
|
18
|
+
|
|
19
|
+
import { Button } from "../Button/index.js";
|
|
20
|
+
|
|
21
|
+
export const Prompt = () => {
|
|
22
|
+
return span({ class: "prompt" })
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function CellItem({ cellData, app, store }) {
|
|
26
|
+
const outputRef = div({ class: "output-area" });
|
|
27
|
+
if (cellData.outputNode) outputRef.append(cellData.outputNode);
|
|
28
|
+
cellData._outputRef = outputRef.element;
|
|
29
|
+
|
|
30
|
+
const actions = app._cellActions(cellData);
|
|
31
|
+
cellData._actions = actions;
|
|
32
|
+
cellData._editor = createCodeEditor(app, cellData, actions);
|
|
33
|
+
|
|
34
|
+
/* Check ziko */
|
|
35
|
+
const [In, setIn] = useState(0);
|
|
36
|
+
globalThis.setIn = setIn;
|
|
37
|
+
const inPrompt = span({ class: "prompt" }, In);
|
|
38
|
+
const outPrompt = span({ class: "prompt out" });
|
|
39
|
+
const bodySlot = div().style({ display: 'contents' });
|
|
40
|
+
|
|
41
|
+
// Pass runtimePlugins from app into CellControls
|
|
42
|
+
const controlsEl = CellControls({
|
|
43
|
+
cellData,
|
|
44
|
+
store,
|
|
45
|
+
actions,
|
|
46
|
+
runtimePlugins: app.runtimePlugins
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const inputRow = div({ class: "cell-input-row" }, inPrompt, bodySlot);
|
|
50
|
+
const outputRow = div({ class: "cell-output-row" }, outPrompt, div({ class: "output-wrapper" }, outputRef));
|
|
51
|
+
const root = div({ class: "cell" }, inputRow, controlsEl, outputRow);
|
|
52
|
+
root.onClick(() => app.setActiveCell(cellData.id));
|
|
53
|
+
|
|
54
|
+
cellData._dom = root.element;
|
|
55
|
+
cellData._inPrompt = inPrompt.element;
|
|
56
|
+
cellData._outPrompt = outPrompt.element;
|
|
57
|
+
cellData._bodySlot = bodySlot.element;
|
|
58
|
+
cellData._outputRow = outputRow.element;
|
|
59
|
+
cellData._setIn = setIn;
|
|
60
|
+
|
|
61
|
+
app._refreshCell(cellData);
|
|
62
|
+
return root.element;
|
|
63
|
+
}
|
|
64
|
+
export function CellControls({ cellData, store, actions, runtimePlugins = [] }) {
|
|
65
|
+
const controlsContainer = div({ class: "cell-controls" }).element;
|
|
66
|
+
|
|
67
|
+
const renderButtons = () => {
|
|
68
|
+
const isQueueRunning = store.get('executionQueueState').isRunning;
|
|
69
|
+
const isMaxCellsReached = store.get('cells').length >= store.get('maxCells');
|
|
70
|
+
const isCopied = cellData._copiedFlag === true;
|
|
71
|
+
|
|
72
|
+
// Build the runtime selector dropdown for code cells using native DOM options
|
|
73
|
+
let runtimeSelectEl = null;
|
|
74
|
+
if (cellData.type === "code") {
|
|
75
|
+
const selectElement = document.createElement("select");
|
|
76
|
+
selectElement.className = "runtime-select";
|
|
77
|
+
selectElement.title = "Select Cell Runtime";
|
|
78
|
+
selectElement.disabled = cellData.readonly;
|
|
79
|
+
|
|
80
|
+
selectElement.onchange = (e) => {
|
|
81
|
+
actions.setRuntime(e.target.value === "" ? null : e.target.value);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Default JS option
|
|
85
|
+
const optJs = document.createElement("option");
|
|
86
|
+
optJs.value = "";
|
|
87
|
+
optJs.textContent = "JS";
|
|
88
|
+
selectElement.appendChild(optJs);
|
|
89
|
+
|
|
90
|
+
// Runtime plugin options
|
|
91
|
+
for (const p of runtimePlugins) {
|
|
92
|
+
const opt = document.createElement("option");
|
|
93
|
+
opt.value = p.name;
|
|
94
|
+
opt.textContent = p.name.charAt(0).toUpperCase() + p.name.slice(1);
|
|
95
|
+
selectElement.appendChild(opt);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Explicitly set the active value
|
|
99
|
+
selectElement.value = cellData.runtime || "";
|
|
100
|
+
runtimeSelectEl = selectElement;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const children = [
|
|
104
|
+
Button({
|
|
105
|
+
class: `btn-icon ${isCopied ? "btn-active" : ""}`,
|
|
106
|
+
title: isCopied ? "Copied!" : "Copy Cell Content",
|
|
107
|
+
onclick: async () => {
|
|
108
|
+
await actions.copyCode();
|
|
109
|
+
cellData._copiedFlag = true;
|
|
110
|
+
renderButtons();
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
cellData._copiedFlag = false;
|
|
113
|
+
renderButtons();
|
|
114
|
+
}, 1500);
|
|
115
|
+
}
|
|
116
|
+
}, isCopied ? Check() : Copy()),
|
|
117
|
+
|
|
118
|
+
Button({
|
|
119
|
+
class: `btn-icon ${cellData.readonly ? "btn-active" : ""}`,
|
|
120
|
+
title: cellData.readonly ? "Make Editable" : "Make Read-Only",
|
|
121
|
+
disabled: isQueueRunning,
|
|
122
|
+
onclick: actions.toggleReadonly,
|
|
123
|
+
}, cellData.readonly ? Lock() : Unlock()),
|
|
124
|
+
|
|
125
|
+
Button({
|
|
126
|
+
class: `btn-icon ${cellData.type === "markdown" ? "btn-active" : ""}`,
|
|
127
|
+
title: `Switch to ${cellData.type === "code" ? "Markdown" : "JS Code"}`,
|
|
128
|
+
disabled: isQueueRunning || cellData.readonly,
|
|
129
|
+
onclick: actions.toggleType,
|
|
130
|
+
}, cellData.type === "code" ? FileText() : Code2()),
|
|
131
|
+
|
|
132
|
+
(cellData.type === "markdown" && !cellData.isEditingMarkdown)
|
|
133
|
+
? Button({
|
|
134
|
+
class: "btn-icon",
|
|
135
|
+
title: "Edit Markdown",
|
|
136
|
+
disabled: isQueueRunning || cellData.readonly,
|
|
137
|
+
onclick: actions.editMarkdown
|
|
138
|
+
}, Pencil())
|
|
139
|
+
: div(
|
|
140
|
+
Button({
|
|
141
|
+
class: "btn-icon",
|
|
142
|
+
title: "Run (Shift+Enter)",
|
|
143
|
+
disabled: isQueueRunning,
|
|
144
|
+
onclick: () => actions.runCode(false)
|
|
145
|
+
}, Refresh()),
|
|
146
|
+
Button({
|
|
147
|
+
class: "btn-icon btn-primary",
|
|
148
|
+
title: "Run & Advance (Shift+Enter)",
|
|
149
|
+
disabled: isQueueRunning,
|
|
150
|
+
onclick: () => actions.runCode(true)
|
|
151
|
+
}, Play())
|
|
152
|
+
).style({ display : 'contents'}).element,
|
|
153
|
+
|
|
154
|
+
Button({
|
|
155
|
+
class: "btn-icon",
|
|
156
|
+
title: "Add Cell Below",
|
|
157
|
+
disabled: isQueueRunning || isMaxCellsReached,
|
|
158
|
+
onclick: actions.addBelow
|
|
159
|
+
}, Plus()),
|
|
160
|
+
|
|
161
|
+
Button({
|
|
162
|
+
class: "btn-icon",
|
|
163
|
+
title: "Delete Cell",
|
|
164
|
+
disabled: isQueueRunning,
|
|
165
|
+
onclick: actions.deleteCell
|
|
166
|
+
}, Trash2())
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
if (runtimeSelectEl) {
|
|
170
|
+
children.unshift(runtimeSelectEl);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
controlsContainer.replaceChildren(...children);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
renderButtons();
|
|
177
|
+
store.on('change', renderButtons);
|
|
178
|
+
|
|
179
|
+
return controlsContainer;
|
|
180
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
.ziko-notebook .error-output {
|
|
2
|
-
color: #e11d48;
|
|
3
|
-
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
4
|
-
white-space: pre-wrap;
|
|
5
|
-
font-size: 12px;
|
|
6
|
-
background: #fff1f2;
|
|
7
|
-
padding: 8px;
|
|
8
|
-
border-radius: 4px;
|
|
9
|
-
border: 1px solid #fecdd3;
|
|
1
|
+
.ziko-notebook .error-output {
|
|
2
|
+
color: #e11d48;
|
|
3
|
+
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
4
|
+
white-space: pre-wrap;
|
|
5
|
+
font-size: 12px;
|
|
6
|
+
background: #fff1f2;
|
|
7
|
+
padding: 8px;
|
|
8
|
+
border-radius: 4px;
|
|
9
|
+
border: 1px solid #fecdd3;
|
|
10
10
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { tags } from "ziko/dom"
|
|
1
|
+
import { tags } from "ziko/dom"
|
|
2
2
|
export const ErrorOutput = (err) => tags.div({ class: "error-output" }, err.toString())
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
/* --- HEADER & TOOLBAR --- */
|
|
2
|
-
|
|
3
|
-
.ziko-notebook .header {
|
|
4
|
-
border-bottom: 1px solid var(--border-color);
|
|
5
|
-
background: #ffffff;
|
|
6
|
-
padding: 10px 16px;
|
|
7
|
-
display: flex;
|
|
8
|
-
align-items: center;
|
|
9
|
-
justify-content: center;
|
|
10
|
-
gap: 12px;
|
|
11
|
-
flex-wrap: wrap;
|
|
12
|
-
position: sticky;
|
|
13
|
-
top: 0;
|
|
14
|
-
z-index: 100;
|
|
15
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
|
|
16
|
-
width: 100%;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.ziko-notebook .brand {
|
|
20
|
-
font-weight: 600;
|
|
21
|
-
font-size: 16px;
|
|
22
|
-
color: #0f172a;
|
|
23
|
-
display: flex;
|
|
24
|
-
align-items: center;
|
|
25
|
-
gap: 12px;
|
|
26
|
-
flex-shrink: 0;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.ziko-notebook .toolbar {
|
|
30
|
-
display: flex;
|
|
31
|
-
gap: 6px;
|
|
32
|
-
align-items: baseline;
|
|
33
|
-
flex-wrap: wrap;
|
|
34
|
-
max-width: 100%;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.ziko-notebook .status-badge {
|
|
38
|
-
font-size: 12px;
|
|
39
|
-
font-weight: 500;
|
|
40
|
-
color: var(--primary);
|
|
41
|
-
background: #e0f2fe;
|
|
42
|
-
padding: 4px 8px;
|
|
43
|
-
border-radius: 4px;
|
|
44
|
-
display: inline-flex;
|
|
45
|
-
align-items: center;
|
|
46
|
-
gap: 6px;
|
|
1
|
+
/* --- HEADER & TOOLBAR --- */
|
|
2
|
+
|
|
3
|
+
.ziko-notebook .header {
|
|
4
|
+
border-bottom: 1px solid var(--border-color);
|
|
5
|
+
background: #ffffff;
|
|
6
|
+
padding: 10px 16px;
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
gap: 12px;
|
|
11
|
+
flex-wrap: wrap;
|
|
12
|
+
position: sticky;
|
|
13
|
+
top: 0;
|
|
14
|
+
z-index: 100;
|
|
15
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
|
|
16
|
+
width: 100%;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.ziko-notebook .brand {
|
|
20
|
+
font-weight: 600;
|
|
21
|
+
font-size: 16px;
|
|
22
|
+
color: #0f172a;
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
gap: 12px;
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ziko-notebook .toolbar {
|
|
30
|
+
display: flex;
|
|
31
|
+
gap: 6px;
|
|
32
|
+
align-items: baseline;
|
|
33
|
+
flex-wrap: wrap;
|
|
34
|
+
max-width: 100%;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.ziko-notebook .status-badge {
|
|
38
|
+
font-size: 12px;
|
|
39
|
+
font-weight: 500;
|
|
40
|
+
color: var(--primary);
|
|
41
|
+
background: #e0f2fe;
|
|
42
|
+
padding: 4px 8px;
|
|
43
|
+
border-radius: 4px;
|
|
44
|
+
display: inline-flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
gap: 6px;
|
|
47
47
|
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import van from 'vanjs-core';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
FileText,
|
|
5
|
-
Plus,
|
|
6
|
-
Trash2,
|
|
7
|
-
PlayForward,
|
|
8
|
-
Eraser,
|
|
9
|
-
Download,
|
|
10
|
-
Loader2
|
|
11
|
-
} from '../icons/index.js';
|
|
12
|
-
|
|
13
|
-
const { button } = van.tags;
|
|
14
|
-
|
|
15
|
-
import { tags } from 'ziko/dom'
|
|
16
|
-
|
|
17
|
-
const { div, span } = tags
|
|
18
|
-
|
|
19
|
-
export const Button = ({ class: className = '', title = '', disabled = false, onclick }, ...children) =>
|
|
20
|
-
button({ class: `btn ${className}`, title, disabled, onclick }, ...children);
|
|
21
|
-
|
|
22
|
-
export function Header({ store, actions }) {
|
|
23
|
-
const statusBadge = span({ class: 'status-badge' }, Loader2(), span('Loading ...'));
|
|
24
|
-
const runAllBtn = Button({ class: 'btn-primary', onclick: actions.runAll, title : 'Run All' }, PlayForward());
|
|
25
|
-
const clearBtn = Button({ onclick: actions.clearOutputs, title : 'Clear Outputs' }, Eraser());
|
|
26
|
-
const addCodeBtn = Button({ onclick: actions.addCode, title : 'Add Code'}, Plus());
|
|
27
|
-
const addMdBtn = Button({ onclick: actions.addMarkdown, title : 'Add Markdown' }, FileText());
|
|
28
|
-
const deleteActiveBtn = Button({ onclick: actions.deleteActive, title : 'Delete Active cell' }, Trash2());
|
|
29
|
-
const exportBtn = Button({ onclick: actions.exportData, title : 'Export Data' }, Download());
|
|
30
|
-
|
|
31
|
-
store.on('change:executionQueueState', (q) => {
|
|
32
|
-
statusBadge.style({display :q.isRunning ? 'inline-flex' : 'none' })
|
|
33
|
-
statusBadge.element.lastChild.textContent = `Executing Queue (${q.currentIndex + 1}/${q.total})`;
|
|
34
|
-
[runAllBtn, clearBtn, deleteActiveBtn].forEach(b => { b.disabled = q.isRunning; });
|
|
35
|
-
const maxReached = store.get('cells').length >= store.get('maxCells');
|
|
36
|
-
addCodeBtn.disabled = q.isRunning || maxReached;
|
|
37
|
-
addMdBtn.disabled = q.isRunning || maxReached;
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
statusBadge.unmount()
|
|
41
|
-
|
|
42
|
-
return div({ class: 'header' },
|
|
43
|
-
// div({ class: 'brand' }, '</>', statusBadge),
|
|
44
|
-
div({ class: 'toolbar' },
|
|
45
|
-
runAllBtn, clearBtn, addCodeBtn, addMdBtn, deleteActiveBtn, exportBtn
|
|
46
|
-
)
|
|
47
|
-
).element;
|
|
1
|
+
import van from 'vanjs-core';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
FileText,
|
|
5
|
+
Plus,
|
|
6
|
+
Trash2,
|
|
7
|
+
PlayForward,
|
|
8
|
+
Eraser,
|
|
9
|
+
Download,
|
|
10
|
+
Loader2
|
|
11
|
+
} from '../icons/index.js';
|
|
12
|
+
|
|
13
|
+
const { button } = van.tags;
|
|
14
|
+
|
|
15
|
+
import { tags } from 'ziko/dom'
|
|
16
|
+
|
|
17
|
+
const { div, span } = tags
|
|
18
|
+
|
|
19
|
+
export const Button = ({ class: className = '', title = '', disabled = false, onclick }, ...children) =>
|
|
20
|
+
button({ class: `btn ${className}`, title, disabled, onclick }, ...children);
|
|
21
|
+
|
|
22
|
+
export function Header({ store, actions }) {
|
|
23
|
+
const statusBadge = span({ class: 'status-badge' }, Loader2(), span('Loading ...'));
|
|
24
|
+
const runAllBtn = Button({ class: 'btn-primary', onclick: actions.runAll, title : 'Run All' }, PlayForward());
|
|
25
|
+
const clearBtn = Button({ onclick: actions.clearOutputs, title : 'Clear Outputs' }, Eraser());
|
|
26
|
+
const addCodeBtn = Button({ onclick: actions.addCode, title : 'Add Code'}, Plus());
|
|
27
|
+
const addMdBtn = Button({ onclick: actions.addMarkdown, title : 'Add Markdown' }, FileText());
|
|
28
|
+
const deleteActiveBtn = Button({ onclick: actions.deleteActive, title : 'Delete Active cell' }, Trash2());
|
|
29
|
+
const exportBtn = Button({ onclick: actions.exportData, title : 'Export Data' }, Download());
|
|
30
|
+
|
|
31
|
+
store.on('change:executionQueueState', (q) => {
|
|
32
|
+
statusBadge.style({display :q.isRunning ? 'inline-flex' : 'none' })
|
|
33
|
+
statusBadge.element.lastChild.textContent = `Executing Queue (${q.currentIndex + 1}/${q.total})`;
|
|
34
|
+
[runAllBtn, clearBtn, deleteActiveBtn].forEach(b => { b.disabled = q.isRunning; });
|
|
35
|
+
const maxReached = store.get('cells').length >= store.get('maxCells');
|
|
36
|
+
addCodeBtn.disabled = q.isRunning || maxReached;
|
|
37
|
+
addMdBtn.disabled = q.isRunning || maxReached;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
statusBadge.unmount()
|
|
41
|
+
|
|
42
|
+
return div({ class: 'header' },
|
|
43
|
+
// div({ class: 'brand' }, '</>', statusBadge),
|
|
44
|
+
div({ class: 'toolbar' },
|
|
45
|
+
runAllBtn, clearBtn, addCodeBtn, addMdBtn, deleteActiveBtn, exportBtn
|
|
46
|
+
)
|
|
47
|
+
).element;
|
|
48
48
|
}
|